Compare commits

..

No commits in common. "70aaa1ff44a66501c27adbe73da7321050515d3e" and "d75242d21394cb764a83ea7a56c29fbbd57bf747" have entirely different histories.

15 changed files with 1360 additions and 856 deletions

1926
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,14 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "3.1.6", features = ["derive", "env"] }
actix-web = "4"
rocket = "0.5.0-rc.1"
rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] }
include_dir = "0.7.2"
log = "0.4.16"
serde_json = "1.0.79"
env_logger = "0.9.0"
serde = { version = "1.0.136", features = ["derive"] }
bcrypt = "0.12.1"
uuid = { version = "0.8.2", features = ["v4"] }
mime_guess = "2.0.4"
askama = "0.11.1"
uuid = { version = "0.8.2", features = ["v4"] }

View File

@ -1,57 +0,0 @@
html,
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
/* background-color: #f5f5f5; */
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-floating:first-child input {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-floating:not(:first-child):not(:last-child) input {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.form-floating:last-child input {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.form-control {
background-color: var(--bs-gray-700);
color: var(--bs-gray-100);
}
.form-control:focus {
background-color: var(--bs-gray-600);
color: var(--bs-gray-100);
}

5
assets/css/login.css Normal file
View File

@ -0,0 +1,5 @@
/**
* Login page
*
* @author Pierre Hubert
*/

View File

@ -1,5 +0,0 @@
// Remove un-used alerts
document.querySelectorAll("[role=alert]").forEach(el => {
if(el.innerHTML.trim() === "")
el.remove();
})

View File

@ -3,7 +3,4 @@ pub const USERS_LIST_FILE: &str = "users.json";
/// Default built-in credentials
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
/// App name
pub const APP_NAME: &str = "Basic OIDC";
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";

View File

@ -1,20 +1,22 @@
use std::path::Path;
use std::path::PathBuf;
use actix_web::{HttpResponse, web};
use include_dir::{Dir, include_dir};
use rocket::http::{ContentType, Status};
/// Assets directory
static ASSETS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/assets");
pub async fn assets_route(path: web::Path<String>) -> HttpResponse {
let path: &Path = path.as_ref().as_ref();
match ASSETS_DIR.get_file(path) {
None => HttpResponse::NotFound().body("404 Not found"),
#[get("/<file..>")]
pub fn assets_route(file: PathBuf) -> (Status, (ContentType, &'static [u8])) {
match ASSETS_DIR.get_file(file) {
None =>
(Status::NotFound, (ContentType::Text, "404 Not found".as_bytes())),
Some(file) => {
let res = mime_guess::from_path(path).first_or_octet_stream();
HttpResponse::Ok()
.content_type(res.to_string())
.body(file.contents())
(Status::Ok, (
ContentType::from_extension(file.path().extension().unwrap_or_default()
.to_string_lossy().as_ref())
.unwrap_or(ContentType::Binary),
file.contents()))
}
}
}

View File

@ -1,35 +0,0 @@
use actix_web::{HttpResponse, Responder};
use askama::Template;
use crate::constants::APP_NAME;
#[derive(Template)]
#[template(path = "base_login_page.html")]
struct BaseLoginPage {
danger: String,
success: String,
page_title: &'static str,
app_name: &'static str,
}
#[derive(Template)]
#[template(path = "login.html")]
struct LoginTemplate {
_parent: BaseLoginPage,
mail: String,
}
pub async fn login_route() -> impl Responder {
HttpResponse::Ok()
.content_type("text/html")
.body(LoginTemplate {
_parent: BaseLoginPage {
page_title: "Login",
danger: "".to_string(),
success: "".to_string(),
app_name: APP_NAME,
},
mail: "".to_string()
}.render().unwrap())
}

View File

@ -1,2 +1 @@
pub mod assets_controller;
pub mod login_controller;
pub mod assets_controller;

View File

@ -1,20 +1,11 @@
use std::path::{Path, PathBuf};
use clap::Parser;
use rocket::serde::Deserialize;
use crate::constants::USERS_LIST_FILE;
/// Basic OIDC provider
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct AppConfig {
/// Listen address
#[clap(short, long, env, default_value = "0.0.0.0:8000")]
pub listen_address: String,
/// Storage path
#[clap(short, long, env)]
pub storage_path: String,
storage_path: PathBuf,
}
impl AppConfig {
@ -23,6 +14,6 @@ impl AppConfig {
}
pub fn users_file(&self) -> PathBuf {
self.storage_path().join(USERS_LIST_FILE)
self.storage_path.join(USERS_LIST_FILE)
}
}

View File

@ -7,7 +7,7 @@ pub struct EntityManager<E> {
list: Vec<E>,
}
impl<E> EntityManager<E> where E: serde::Serialize + serde::de::DeserializeOwned + Eq + Clone {
impl<E> EntityManager<E> where E: rocket::serde::Serialize + rocket::serde::DeserializeOwned + Eq + Clone {
/// Open entity
pub fn open_or_create<A: AsRef<Path>>(path: A) -> Res<Self> {
if !path.as_ref().is_file() {

View File

@ -1,3 +1,7 @@
#[macro_use]
extern crate rocket;
pub mod data;
pub mod utils;
pub mod constants;

View File

@ -1,24 +1,31 @@
use actix_web::{App, get, HttpServer, web};
use actix_web::middleware::Logger;
use clap::Parser;
#[macro_use]
extern crate rocket;
use rocket::fairing::AdHoc;
use basic_oidc::constants::{DEFAULT_ADMIN_PASSWORD, DEFAULT_ADMIN_USERNAME};
use basic_oidc::controllers::assets_controller::assets_route;
use basic_oidc::controllers::login_controller::login_route;
use basic_oidc::data::app_config::AppConfig;
use basic_oidc::data::entity_manager::EntityManager;
use basic_oidc::data::user::{hash_password, User};
use basic_oidc::controllers::assets_controller::assets_route;
#[get("/health")]
async fn health() -> &'static str {
fn index() -> &'static str {
"Running"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
//env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let config: AppConfig = AppConfig::parse();
let rocket = rocket::build()
.mount("/", routes![index])
.mount("/assets", routes![assets_route])
.attach(AdHoc::config::<AppConfig>());
let figment = rocket.figment();
// Initialize application
let config: AppConfig = figment.extract().expect("config");
if !config.storage_path().exists() {
log::error!(
@ -48,16 +55,5 @@ async fn main() -> std::io::Result<()> {
.expect("Failed to create initial user!");
}
log::info!("Server will listen on {}", config.listen_address);
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.service(health)
.route("/assets/{path:.*}", web::get().to(assets_route))
.route("/login", web::get().to(login_route))
})
.bind(config.listen_address)?
.run()
.await
rocket.launch().await
}

View File

@ -1,63 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Auth service">
<title>{{ app_name }} - {{ page_title }}</title>
<!-- Bootstrap core CSS -->
<link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/>
<!-- Favicons -->
<meta name="theme-color" content="#7952b3">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="/assets/css/base_login_page.css" rel="stylesheet">
</head>
<body class="text-center">
<main class="form-signin">
<h1 class="h3 mb-3 fw-normal">{{ page_title }}</h1>
<div class="alert alert-danger" role="alert">
{{ danger }}
</div>
<div class="alert alert-success" role="alert">
{{ success }}
</div>
{% block content %}
TO_REPLACE
{% endblock content %}
<p class="mt-5 mb-3 text-muted">&copy; 2022 -
<script>document.write(new Date().getFullYear())</script>
</p>
</main>
<script src="/assets/js/base_login_page.js"></script>
</body>
</html>

View File

@ -1,22 +0,0 @@
{% extends "base_login_page.html" %}
{% block content %}
<form action="/login" method="post">
<div>
<div class="form-floating">
<input name="mail" type="text" required class="form-control" id="floatingName" placeholder="unsername"
value="{{ mail }}">
<label for="floatingName">Email address or username</label>
</div>
<div class="form-floating">
<input name="password" type="password" required class="form-control" id="floatingPassword"
placeholder="Password">
<label for="floatingPassword">Password</label>
</div>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>
{% endblock content %}