Store jwt secret in database
This commit is contained in:
parent
95914653e0
commit
7cc19bc1cd
|
@ -59,9 +59,8 @@ pub struct AuthResponse {
|
||||||
issued_at: String,
|
issued_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In the returned UserToken::user, only the username is specified
|
fn create_jwt_token(jwt_key: String, account: Option<&str>, scopes: Vec<Scope>) -> anyhow::Result<TokenInfo> {
|
||||||
fn create_jwt_token(account: Option<&str>, scopes: Vec<Scope>) -> anyhow::Result<TokenInfo> {
|
let key: Hmac<Sha256> = Hmac::new_from_slice(jwt_key.as_bytes())?;
|
||||||
let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
|
|
||||||
|
|
||||||
let now = chrono::offset::Utc::now();
|
let now = chrono::offset::Utc::now();
|
||||||
|
|
||||||
|
@ -186,7 +185,8 @@ pub async fn auth_basic_get(
|
||||||
scope.actions.retain(|a| *a == Action::Pull);
|
scope.actions.retain(|a| *a == Action::Pull);
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = create_jwt_token(None, auth.scope).map_err(|_| {
|
let token = create_jwt_token(state.config.jwt_key.clone(), None, auth.scope)
|
||||||
|
.map_err(|_| {
|
||||||
error!("Failed to create jwt token!");
|
error!("Failed to create jwt token!");
|
||||||
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
@ -290,7 +290,8 @@ pub async fn auth_basic_get(
|
||||||
debug!("User password is correct");
|
debug!("User password is correct");
|
||||||
|
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let token = create_jwt_token(Some(account), vec![]).map_err(|_| {
|
let token = create_jwt_token(state.config.jwt_key.clone(), Some(account), vec![])
|
||||||
|
.map_err(|_| {
|
||||||
error!("Failed to create jwt token!");
|
error!("Failed to create jwt token!");
|
||||||
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
|
|
@ -71,6 +71,8 @@ pub struct Config {
|
||||||
pub database: DatabaseConfig,
|
pub database: DatabaseConfig,
|
||||||
pub storage: StorageConfig,
|
pub storage: StorageConfig,
|
||||||
pub tls: Option<TlsConfig>,
|
pub tls: Option<TlsConfig>,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub jwt_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sqlx::{Sqlite, Pool};
|
use hmac::{Hmac, digest::KeyInit};
|
||||||
|
use rand::{Rng, distributions::Alphanumeric};
|
||||||
|
use sha2::Sha256;
|
||||||
|
use sqlx::{Sqlite, Pool, sqlite::SqliteError};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc, NaiveDateTime, TimeZone};
|
use chrono::{DateTime, Utc, NaiveDateTime, TimeZone};
|
||||||
|
@ -8,12 +11,13 @@ use crate::dto::{Tag, user::{User, RepositoryPermissions, RegistryUserType, Perm
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Database {
|
pub trait Database {
|
||||||
|
|
||||||
// Digest related functions
|
// Digest related functions
|
||||||
|
|
||||||
/// Create the tables in the database
|
/// Create the tables in the database
|
||||||
async fn create_schema(&self) -> anyhow::Result<()>;
|
async fn create_schema(&self) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
async fn get_jwt_secret(&self) -> anyhow::Result<String>;
|
||||||
|
|
||||||
// Tag related functions
|
// Tag related functions
|
||||||
|
|
||||||
/// Get tags associated with a repository
|
/// Get tags associated with a repository
|
||||||
|
@ -67,14 +71,56 @@ pub trait Database {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Database for Pool<Sqlite> {
|
impl Database for Pool<Sqlite> {
|
||||||
async fn create_schema(&self) -> anyhow::Result<()> {
|
async fn create_schema(&self) -> anyhow::Result<()> {
|
||||||
|
let orca_version = "0.1.0";
|
||||||
|
let schema_version = "0.0.1";
|
||||||
|
|
||||||
|
let row: Option<(u32, )> = match sqlx::query_as("SELECT COUNT(1) FROM orca WHERE \"schema_version\" = ?")
|
||||||
|
.bind(schema_version)
|
||||||
|
.fetch_one(self).await {
|
||||||
|
Ok(row) => Some(row),
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
// ignore no such table errors
|
||||||
|
sqlx::Error::Database(b) if b.message().starts_with("no such table") => None,
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow::Error::new(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if row.is_none() || row.unwrap().0 == 0 {
|
||||||
|
let jwt_sec: String = rand::thread_rng()
|
||||||
|
.sample_iter(&Alphanumeric)
|
||||||
|
.take(16)
|
||||||
|
.map(char::from)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// create schema
|
||||||
|
// TODO: Check if needed
|
||||||
sqlx::query(include_str!("schemas/schema.sql"))
|
sqlx::query(include_str!("schemas/schema.sql"))
|
||||||
.execute(self).await?;
|
.execute(self).await?;
|
||||||
|
|
||||||
debug!("Created database schema");
|
debug!("Created database schema");
|
||||||
|
|
||||||
|
sqlx::query("INSERT INTO orca(orca_version, schema_version, jwt_secret) VALUES (?, ?, ?)")
|
||||||
|
.bind(orca_version)
|
||||||
|
.bind(schema_version)
|
||||||
|
.bind(jwt_sec)
|
||||||
|
.execute(self).await?;
|
||||||
|
debug!("Inserted information about orca!");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_jwt_secret(&self) -> anyhow::Result<String> {
|
||||||
|
let rows: (String, ) = sqlx::query_as("SELECT jwt_secret FROM orca WHERE id = (SELECT max(id) FROM orca)")
|
||||||
|
.fetch_one(self).await?;
|
||||||
|
|
||||||
|
Ok(rows.0)
|
||||||
|
}
|
||||||
|
|
||||||
async fn link_manifest_layer(&self, manifest_digest: &str, layer_digest: &str) -> anyhow::Result<()> {
|
async fn link_manifest_layer(&self, manifest_digest: &str, layer_digest: &str) -> anyhow::Result<()> {
|
||||||
sqlx::query("INSERT INTO manifest_layers(manifest, layer_digest) VALUES (?, ?)")
|
sqlx::query("INSERT INTO manifest_layers(manifest, layer_digest) VALUES (?, ?)")
|
||||||
.bind(manifest_digest)
|
.bind(manifest_digest)
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS orca (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
orca_version TEXT NOT NULL,
|
||||||
|
schema_version TEXT NOT NULL,
|
||||||
|
jwt_secret TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS projects (
|
CREATE TABLE IF NOT EXISTS projects (
|
||||||
name TEXT NOT NULL UNIQUE PRIMARY KEY,
|
name TEXT NOT NULL UNIQUE PRIMARY KEY,
|
||||||
-- 0 = private, 1 = public
|
-- 0 = private, 1 = public
|
||||||
|
|
|
@ -69,7 +69,7 @@ async fn change_request_paths<B>(mut request: Request<B>, next: Next<B>) -> Resu
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let config = Config::new()
|
let mut config = Config::new()
|
||||||
.expect("Failure to parse config!");
|
.expect("Failure to parse config!");
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
@ -92,6 +92,9 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.connect_with(connection_options).await?;
|
.connect_with(connection_options).await?;
|
||||||
pool.create_schema().await?;
|
pool.create_schema().await?;
|
||||||
|
|
||||||
|
// set jwt key
|
||||||
|
config.jwt_key = pool.get_jwt_secret().await?;
|
||||||
|
|
||||||
let storage_driver: Mutex<Box<dyn StorageDriver>> = match &config.storage {
|
let storage_driver: Mutex<Box<dyn StorageDriver>> = match &config.storage {
|
||||||
StorageConfig::Filesystem(fs) => {
|
StorageConfig::Filesystem(fs) => {
|
||||||
Mutex::new(Box::new(FilesystemDriver::new(&fs.path)))
|
Mutex::new(Box::new(FilesystemDriver::new(&fs.path)))
|
||||||
|
|
Loading…
Reference in New Issue