diff --git a/Cargo.lock b/Cargo.lock
index 7b36c01..8cd9a61 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -429,7 +429,7 @@ dependencies = [
"http",
"http-body",
"hyper",
- "hyper-rustls 0.23.2",
+ "hyper-rustls",
"lazy_static",
"pin-project-lite",
"rustls 0.20.9",
@@ -605,6 +605,12 @@ dependencies = [
"rustc-demangle",
]
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
[[package]]
name = "base64"
version = "0.21.7"
@@ -713,6 +719,7 @@ dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
+ "serde",
"windows-targets 0.52.5",
]
@@ -767,7 +774,7 @@ dependencies = [
"anstream",
"anstyle",
"clap_lex",
- "strsim",
+ "strsim 0.11.1",
]
[[package]]
@@ -908,6 +915,41 @@ dependencies = [
"cipher",
]
+[[package]]
+name = "darling"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.60",
+]
+
[[package]]
name = "deranged"
version = "0.3.11"
@@ -915,6 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
+ "serde",
]
[[package]]
@@ -1440,22 +1483,6 @@ dependencies = [
"tokio-rustls 0.23.4",
]
-[[package]]
-name = "hyper-rustls"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
-dependencies = [
- "futures-util",
- "http",
- "hyper",
- "log",
- "rustls 0.21.11",
- "rustls-native-certs",
- "tokio",
- "tokio-rustls 0.24.1",
-]
-
[[package]]
name = "hyper-timeout"
version = "0.4.1"
@@ -1504,6 +1531,12 @@ dependencies = [
"cc",
]
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
[[package]]
name = "idna"
version = "0.5.0"
@@ -1538,6 +1571,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
+ "serde",
]
[[package]]
@@ -1636,6 +1670,24 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "jwtk"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f968792e4a4e8ffd2fcd6adab9a096c6bdb42eb584a0ee7068ec91c62a08ff65"
+dependencies = [
+ "base64 0.13.1",
+ "foreign-types",
+ "openssl",
+ "openssl-sys",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "smallvec",
+ "tokio",
+]
+
[[package]]
name = "kqueue"
version = "1.0.8"
@@ -2541,24 +2593,6 @@ dependencies = [
"uncased",
]
-[[package]]
-name = "rocket_oauth2"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "428be05365d3f1e10eeb4659c7635d365449ec699778bf4c1b0a246cab5c235b"
-dependencies = [
- "async-trait",
- "base64 0.21.7",
- "hyper",
- "hyper-rustls 0.24.2",
- "log",
- "rand",
- "rocket",
- "serde",
- "serde_json",
- "url",
-]
-
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@@ -2576,9 +2610,9 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.33"
+version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
@@ -2775,6 +2809,36 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_with"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
+dependencies = [
+ "base64 0.21.7",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.2.6",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
@@ -2899,6 +2963,12 @@ dependencies = [
"loom",
]
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -3552,12 +3622,12 @@ dependencies = [
"clap",
"env_logger",
"etcd-client",
+ "jwtk",
"log",
"rand",
"reqwest",
"rocket",
"rocket_dyn_templates",
- "rocket_oauth2",
"serde",
"serde_json",
"serde_yaml",
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7f5d1bf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# Vicky
+
+Vicky, which is the babysitter of Timmy, Cosmo and Wanda, is a CD tool for environments with many constraints and dependencies that usually cannot be represented.
+
+
+## Components
+
+Vicky consists out of multiple components to make a spreaded deployment possible.
+
++ vicky
+ + Main Task Scheduler
++ vicky-worker
+ + Task Worker, can run multiple times.
++ dashboard
+ + Web-UI
++ vicky-cli
+ + CLI
+
+Each component can be developed and deployed individually.
+
+## Concepts
+
+We use an etcd cluster to sync state between multiple instances of Vicky. Vicky will do leader election, so at each time only one instance is active. We try to make Vicky as resilient to network and other failues as possible but it is not our main goal, yet.
+All data in the etcd is stored under `vicky.wobcom.de/` in YAML format.
+
+## Development Setup
+
+We need to start at least a `vicky` instance, S3 storage and etcd to run anything.
+
+### Storage & Database & Certificates
+
+#### docker-compose
+
++ Generate TLS client certificates for etcd authentication
+ + `nix run .\#generate-certs`
+ + Certificates are located at `certs`
++ Enter `deployment`
++ Start docker-compose collection
+ + `docker-compose up -d`
+
+#### devenv
+
+TODO @yu-re-ka: Add Information
+
+### Vicky
+
++ Copy `vicky/Rocket.example.toml` to `vicky/Rocket.toml`
+ + `Rocket.example.toml` contains the correct configuration to run with the provided development environment.
++ Edit `vicky/Rocket.toml`
+ + Add own machine token to configuration
+ + This is needed for `vicky-worker` later.
+ + Add OIDC authentication provider to configuration
++ Enter `vicky`
++ Run `cargo run --bin vicky`
+
+
+### Vicky Worker
+
++ Copy `vicky-worker/Rocket.example.toml` to `vicky-worker/Rocket.toml`
++ Edit `vicky-worker/Rocket.toml`
+ + Add `machine_token` from last step into this configuration.
++ Enter `vicky-worker`
++ Run `cargo run --bin vicky-worker`
+
+### Dashboard
+
++ Enter `dashboard`
++ Install Dependencies
+ + `npm ci` in `dashboard` Folder
++ Run `npm run start`
+
+### CLI
+
+TODO: Add Content for CLI configuration and development.
+
+
+
diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json
index 060c8f3..8080369 100644
--- a/dashboard/package-lock.json
+++ b/dashboard/package-lock.json
@@ -11,9 +11,11 @@
"dependencies": {
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.5.0",
+ "oidc-client-ts": "^3.0.0-beta.0",
"parcel": "^2.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-oidc-context": "^3.0.0-beta.0",
"react-router-dom": "^6.15.0",
"rsuite": "^5.39.0",
"xterm": "^5.3.0",
@@ -2871,6 +2873,14 @@
"node": ">=6"
}
},
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/lightningcss": {
"version": "1.21.8",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.21.8.tgz",
@@ -3264,6 +3274,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/oidc-client-ts": {
+ "version": "3.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.0-beta.0.tgz",
+ "integrity": "sha512-LXd4/w6kzYe0Hc2d+orHR9ACmRVgUOtWxOttca5DoZgvWU1tWlMbIKfiiKRRLOsHejc2y28Q+JhvGofK8gfXyQ==",
+ "dependencies": {
+ "jwt-decode": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/ordered-binary": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.4.1.tgz",
@@ -3459,6 +3480,18 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/react-oidc-context": {
+ "version": "3.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-3.0.0-beta.0.tgz",
+ "integrity": "sha512-Y3WjJ+2qBpNqkX7DTnYc2WRLhXajX2tCv2S5rSB05J9IOjAOBYPXArIlHbjp6UWnSBW8rGbobmoRB9clrqIcWg==",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "oidc-client-ts": "^3.0.0-beta.0",
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/react-refresh": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
diff --git a/dashboard/package.json b/dashboard/package.json
index ac04f68..f579251 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -17,9 +17,11 @@
"dependencies": {
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.5.0",
+ "oidc-client-ts": "^3.0.0-beta.0",
"parcel": "^2.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-oidc-context": "^3.0.0-beta.0",
"react-router-dom": "^6.15.0",
"rsuite": "^5.39.0",
"xterm": "^5.3.0",
diff --git a/dashboard/src/app.tsx b/dashboard/src/app.tsx
index c404a27..0e7fea2 100644
--- a/dashboard/src/app.tsx
+++ b/dashboard/src/app.tsx
@@ -7,18 +7,28 @@ import { Tasks } from './components/tasks';
import { UserProvider } from './contexts/user';
import { Content } from './content';
import { CustomProvider } from 'rsuite';
+import { AuthProvider } from 'react-oidc-context';
const App = () => {
+ const oidcConfig = {
+ authority: "https://id.lab.wobcom.de/realms/wobcom/",
+ client_id: "vicky-dev",
+ redirect_uri: "http://localhost:1234",
+ onSigninCallback: (): void => {
+ window.history.replaceState({}, document.title, window.location.pathname);
+ },
+ };
+
return (
<>
-
+
-
+
>
)
diff --git a/dashboard/src/components/login.tsx b/dashboard/src/components/login.tsx
index 47e6ae8..04008f6 100644
--- a/dashboard/src/components/login.tsx
+++ b/dashboard/src/components/login.tsx
@@ -2,10 +2,13 @@ import { Button, Col, FlexboxGrid, Panel, Stack } from "rsuite"
import GitHubIcon from '@rsuite/icons/legacy/Github';
import * as s from "./login.module.css";
+import { useAuth } from "react-oidc-context";
const Login = () => {
+ const auth = useAuth();
+
return (
{
Wenn der Account nicht freigeschaltet ist, ist ein Einloggen nicht möglich.
- }>
- Login With GitHub
+
diff --git a/dashboard/src/components/menu.tsx b/dashboard/src/components/menu.tsx
index 6f2b9b8..3becf2d 100644
--- a/dashboard/src/components/menu.tsx
+++ b/dashboard/src/components/menu.tsx
@@ -8,12 +8,14 @@ import { Link, useNavigate } from "react-router-dom";
import { useContext, useEffect, useState } from "react";
import { useAPI } from "../services/api";
import { UserContext } from "../contexts/user";
+import { useAuth } from "react-oidc-context";
const Menu = () => {
const activeKey = "tasks";
const user = useContext(UserContext)
+ const auth = useAuth();
return (
@@ -38,7 +40,7 @@ const Menu = () => {
) : null}
{ !user ? (
-
+ auth.signinRedirect()}>
Login With GitHub
): null}
diff --git a/dashboard/src/content.tsx b/dashboard/src/content.tsx
index 9030eb0..253d36b 100644
--- a/dashboard/src/content.tsx
+++ b/dashboard/src/content.tsx
@@ -1,41 +1,62 @@
-import { useContext } from "react"
+import { ReactNode, useContext } from "react"
import { Navigate, Route, Routes } from "react-router-dom"
import { Login } from "./components/login"
import { Menu } from "./components/menu"
import { Tasks } from "./components/tasks"
-import { UserContext } from "./contexts/user"
+import { UserContext, UserProvider } from "./contexts/user"
import * as s from "./content.module.css"
+import { useAuth } from "react-oidc-context"
const Content = () => {
- const user = useContext(UserContext)
+ const auth = useAuth();
- return (
- <>
-
-
+ switch (auth.activeNavigator) {
+ case "signinSilent":
+ return
Signing you in...
;
+ case "signoutRedirect":
+ return
Signing you out...
;
+ }
- {
- user ? (
+ if (auth.isLoading) {
+ return
Loading...
;
+ }
+
+ if (auth.error) {
+ return
Oops... {auth.error.message}
;
+ }
+
+ if (auth.isAuthenticated) {
+ return (
+
+
+
}>
-
- ) : (
+
+
+
+ );
+ } else {
+ return (
+ <>
+
+
+
- )
+
+ >
+ )
+ }
- }
-
- >
- )
}
export {
diff --git a/dashboard/src/contexts/user.tsx b/dashboard/src/contexts/user.tsx
index 6784a9c..32f0104 100644
--- a/dashboard/src/contexts/user.tsx
+++ b/dashboard/src/contexts/user.tsx
@@ -1,15 +1,15 @@
import { createContext, PropsWithChildren, useEffect, useState } from "react";
-import { useAPI, User } from "../services/api";
+import { useAPI, IUser } from "../services/api";
-const defaultVal: User | null = null
-const UserContext = createContext(null)
+const defaultVal: IUser | null = null
+const UserContext = createContext(null)
const UserProvider = (props: PropsWithChildren) => {
const api = useAPI();
- const [user, setUser] = useState(null);
+ const [user, setUser] = useState(null);
const [userFetched, setUserFetched] = useState(false);
useEffect(() => {
diff --git a/dashboard/src/services/api.tsx b/dashboard/src/services/api.tsx
index 8d2ec97..8035c35 100644
--- a/dashboard/src/services/api.tsx
+++ b/dashboard/src/services/api.tsx
@@ -1,5 +1,6 @@
import axios, { Axios } from "axios"
import { useMemo } from "react"
+import { useAuth } from "react-oidc-context"
type ITask = {
id: string,
@@ -23,20 +24,39 @@ const useAPI = () => {
const BASE_URL = "/api"
+ const auth = useAuth();
+
+ const fetchJSON = async (url: string) => {
+ const authToken = auth.user?.access_token;
+
+ if(!authToken) {
+ throw Error("Using useAPI without an authenticated user is not possible")
+ }
+
+ return fetch(
+ url,
+ {
+ headers: {
+ "Authorization": `Bearer ${authToken}`
+ }
+ }
+ ).then(x => x.json());
+ }
+
const getTasks = (): Promise => {
- return fetch(`${BASE_URL}/tasks`).then(x => x.json());
+ return fetchJSON(`${BASE_URL}/tasks`);
}
const getTask = (id: string): Promise => {
- return fetch(`${BASE_URL}/tasks/${id}`).then(x => x.json());
+ return fetchJSON(`${BASE_URL}/tasks/${id}`);
}
const getTaskLogs = (id: string) => {
- return fetch(`${BASE_URL}/tasks/${id}/logs`).then(x => x.json());
+ return fetchJSON(`${BASE_URL}/tasks/${id}/logs`);
}
const getUser = (): Promise => {
- return fetch(`${BASE_URL}/user`).then(x => x.json());
+ return fetchJSON(`${BASE_URL}/user`);
}
return {
diff --git a/vicky-worker/Rocket.example.toml b/vicky-worker/Rocket.example.toml
index 3c6097e..f3e59d9 100644
--- a/vicky-worker/Rocket.example.toml
+++ b/vicky-worker/Rocket.example.toml
@@ -1,5 +1,6 @@
[default]
-vicky_url = ""
+vicky_url = "http://localhost:8000"
vicky_external_url = "https://vicky.lab.wobcom.de"
-machine_token = ""
\ No newline at end of file
+machine_token = ""
+features = []
\ No newline at end of file
diff --git a/vicky-worker/src/main.rs b/vicky-worker/src/main.rs
index 32718e6..bf3773f 100644
--- a/vicky-worker/src/main.rs
+++ b/vicky-worker/src/main.rs
@@ -9,14 +9,15 @@ use tokio::process::Command;
use tokio_util::codec::{FramedRead, LinesCodec};
use uuid::Uuid;
-
+use rocket::figment::providers::{Env, Format, Toml};
use rocket::figment::{Figment, Profile};
-use rocket::figment::providers::{Toml, Env, Format};
+
#[derive(Deserialize)]
pub(crate) struct AppConfig {
pub(crate) vicky_url: String,
pub(crate) vicky_external_url: String,
pub(crate) machine_token: String,
+ pub(crate) features: Vec,
}
fn main() -> anyhow::Result<()> {
@@ -25,10 +26,18 @@ fn main() -> anyhow::Result<()> {
// Took from rocket source code and added .split("__") to be able to add keys in nested structures.
let rocket_config_figment = Figment::from(rocket::Config::default())
.merge(Toml::file(Env::var_or("ROCKET_CONFIG", "Rocket.toml")).nested())
- .merge(Env::prefixed("ROCKET_").ignore(&["PROFILE"]).split("__").global())
- .select(Profile::from_env_or("ROCKET_PROFILE", rocket::Config::DEFAULT_PROFILE));
-
- let app_config = rocket_config_figment.extract::()?;
+ .merge(
+ Env::prefixed("ROCKET_")
+ .ignore(&["PROFILE"])
+ .split("__")
+ .global(),
+ )
+ .select(Profile::from_env_or(
+ "ROCKET_PROFILE",
+ rocket::Config::DEFAULT_PROFILE,
+ ));
+
+ let app_config = rocket_config_figment.extract::()?;
run(app_config)
}
@@ -39,7 +48,7 @@ async fn api(
q: &Q,
) -> anyhow::Result {
let client = Client::new();
- let req_data = serde_json::to_vec(&q)?;
+ let req_data = serde_json::to_vec(q)?;
let request = Request::builder()
.uri(format!("{}/{}", cfg.vicky_url, endpoint))
@@ -87,7 +96,10 @@ pub struct Task {
pub flake_ref: FlakeRef,
}
-fn log_sink(cfg: Arc, task_id: Uuid) -> impl Sink, Error = anyhow::Error> + Send {
+fn log_sink(
+ cfg: Arc,
+ task_id: Uuid,
+) -> impl Sink, Error = anyhow::Error> + Send {
futures_util::sink::unfold((), move |_, lines: Vec| {
println!("{}", lines.len());
let cfg = cfg.clone();
@@ -104,11 +116,7 @@ fn log_sink(cfg: Arc, task_id: Uuid) -> impl Sink, Error
}
async fn try_run_task(cfg: Arc, task: &Task) -> anyhow::Result<()> {
- let mut args = vec![
- "run".into(),
- "-L".into(),
- task.flake_ref.flake.clone(),
- ];
+ let mut args = vec!["run".into(), "-L".into(), task.flake_ref.flake.clone()];
args.extend(task.flake_ref.args.clone());
let mut child = Command::new("nix")
@@ -163,8 +171,13 @@ async fn run_task(cfg: Arc, task: Task) {
}
async fn try_claim(cfg: Arc) -> anyhow::Result<()> {
- if let Some(task) =
- api::<_, Option>(&cfg, Method::POST, "api/v1/tasks/claim", &None::).await?
+ if let Some(task) = api::<_, Option>(
+ &cfg,
+ Method::POST,
+ "api/v1/tasks/claim",
+ &serde_json::json!({ "features": cfg.features }),
+ )
+ .await?
{
log::info!("task claimed: {} {} 🎉", task.id, task.display_name);
log::debug!("{:#?}", task);
diff --git a/vicky/Cargo.toml b/vicky/Cargo.toml
index cf157e9..3695fe5 100644
--- a/vicky/Cargo.toml
+++ b/vicky/Cargo.toml
@@ -23,8 +23,8 @@ aws-config = "0.55.3"
uuid = { version="1.4.1", features = ["fast-rng", "v4", "serde"] }
rocket = { version="0.5.0", features = ["json", "secrets"] }
rocket_dyn_templates = { version = "0.1.0", features = ["tera"] }
-rocket_oauth2 = "0.5.0-rc.2"
reqwest = { version="0.11.20", features = ["json"]}
+jwtk = "0.3.0"
[[bin]]
name = "vicky"
diff --git a/vicky/README.md b/vicky/README.md
deleted file mode 100644
index 67f7da7..0000000
--- a/vicky/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Vicky
-
-Vicky, which is the babysitter of Timmy, Cosmo and Wanda, is a CD tool for environments with many constraints and dependencies that usually cannot be represented.
-
-## Background
-
-We use an etcd cluster to sync state between multiple instances of Vicky. Vicky will do leader election, so at each time only one instance is active. We try to make Vicky as resilient to network and other failues as possible but it is not our main goal, yet.
-All data in the etcd is stored under `vicky.wobcom.de/` in YAML format.
-
-## Development Usage
-
-+ Start etcd in Docker
- + `cd deployment`
- + `docker-compose up -d`
-+ Start vicky
- + `cargo run --bin vicky`
-
-Make sure to set the correct rust log flag according to your needs.
\ No newline at end of file
diff --git a/vicky/Rocket.example.toml b/vicky/Rocket.example.toml
index a06a1fa..28a1709 100644
--- a/vicky/Rocket.example.toml
+++ b/vicky/Rocket.example.toml
@@ -7,9 +7,9 @@ machines = [
[default.etcd_config]
endpoints = [ "https://localhost:2379" ]
[default.etcd_config.tls_options]
-ca_file = "./certs/Vicky_CA.crt"
-certificate_file = "./certs/Vicky.crt"
-key_file = "./certs/Vicky.key"
+ca_file = "../certs/Vicky_CA.crt"
+certificate_file = "../certs/Vicky.crt"
+key_file = "../certs/Vicky.key"
[default.s3_config]
endpoint = "http://localhost:9000"
@@ -18,14 +18,6 @@ secret_access_key = "aichudiKohr6aithi4ahh3aeng2eL7xo"
region = "us-east-1"
log_bucket = "vicky-logs"
-[default.oauth.github]
-provider = "GitHub"
-client_id = "xxx"
-client_secret = "xxx"
-redirect_uri = "http://localhost:1234/api/auth/callback/github"
-
-
-[default.users.johannwagner]
-full_name = "Johann Wagner"
-role = "admin"
+[default.oidc_config]
+jwks_url = ""
diff --git a/vicky/src/bin/vicky/auth.rs b/vicky/src/bin/vicky/auth.rs
index 09da726..b2fd5e8 100644
--- a/vicky/src/bin/vicky/auth.rs
+++ b/vicky/src/bin/vicky/auth.rs
@@ -1,63 +1,56 @@
-use anyhow::{Context, Error};
-use reqwest::header::{ACCEPT, AUTHORIZATION, USER_AGENT};
-use rocket::http::{Cookie, CookieJar, SameSite};
+use jwtk::jwk::RemoteJwksVerifier;
+use log::{debug, warn};
+use rocket::http::Status;
use rocket::{request, State};
-use rocket::response::{Debug, Redirect};
-use rocket::{get};
-use rocket_oauth2::{OAuth2, TokenResponse};
use serde::Deserialize;
-use rocket::http::Status;
+use serde_json::{Map, Value};
use crate::Config;
#[derive(Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum Role {
- Admin
+ Admin,
}
#[derive(Deserialize)]
pub struct User {
pub full_name: String,
- pub role: Role
+ pub role: Role,
}
-pub struct Machine {
-
-}
+pub struct Machine {}
#[rocket::async_trait]
impl<'r> request::FromRequest<'r> for User {
type Error = ();
async fn from_request(request: &'r request::Request<'_>) -> request::Outcome {
-
- let cookies = request
- .guard::<&CookieJar<'_>>()
+ let jwks_verifier: &State<_> = request
+ .guard::<&State>()
.await
- .expect("request cookies");
+ .expect("request KeyStore");
- let config = request
- .guard::<&State>()
- .await
- .expect("request Config");
+ if let Some(auth_header) = request.headers().get_one("Authorization") {
+ if !auth_header.starts_with("Bearer") {
+ return request::Outcome::Forward(Status::Forbidden);
+ }
- if let Some(cookie) = cookies.get_private("vicky_username") {
+ let token = auth_header.trim_start_matches("Bearer ");
- let username = cookie.value().to_string();
-
- let cfg_user = config.users.get(&username);
- match cfg_user {
- Some(cfg_user) => {
- return request::Outcome::Success(User {
- full_name: cfg_user.full_name.clone(),
- role: cfg_user.role.clone(),
+ return match jwks_verifier.verify::