Rust implementation of OCI Distribution Spec with granular access control
1use serde::{Deserialize, Serialize};
2use tokio::sync::Mutex;
3use utoipa::ToSchema;
4
5use std::{collections::HashSet, fmt, fs};
6
7use crate::args::Args;
8
9#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
10pub(crate) enum ServerStatus {
11 Starting,
12 Ready,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)]
16pub struct Permission {
17 pub repository: String,
18 pub tag: String,
19 pub actions: Vec<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)]
23pub struct User {
24 pub username: String,
25 pub password: String,
26 #[serde(default)]
27 pub permissions: Vec<Permission>,
28}
29
30#[derive(Debug, Serialize, Deserialize, ToSchema)]
31pub struct UsersFile {
32 pub users: Vec<User>,
33}
34
35impl fmt::Display for ServerStatus {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 ServerStatus::Starting => write!(f, "Starting"),
39 ServerStatus::Ready => write!(f, "Ready"),
40 }
41 }
42}
43
44pub(crate) struct App {
45 pub(crate) server_status: Mutex<ServerStatus>,
46 pub(crate) users: Mutex<HashSet<User>>,
47 pub(crate) args: Args,
48}
49
50fn load_users_from_file(file_path: &str) -> HashSet<User> {
51 let file_content = match fs::read_to_string(file_path) {
52 Ok(content) => content,
53 Err(err) => {
54 log::error!("Failed to read users file {}: {}", file_path, err);
55 return HashSet::new();
56 }
57 };
58
59 let users_file: UsersFile = match serde_json::from_str(&file_content) {
60 Ok(users_file) => users_file,
61 Err(err) => {
62 log::error!(
63 "Failed to parse JSON from users file {}: {}",
64 file_path,
65 err
66 );
67 return HashSet::new();
68 }
69 };
70
71 log::info!("Loaded {} users", users_file.users.len());
72 HashSet::from_iter(users_file.users)
73}
74
75pub(crate) fn new_app(args: &Args) -> App {
76 App {
77 server_status: Mutex::new(ServerStatus::Starting),
78 users: Mutex::new(load_users_from_file(&args.users_file)),
79 args: args.clone(),
80 }
81}