Convert datetime to integer
Add roles to admin user profile screen
This commit is contained in:
parent
9870b11664
commit
e7d0780b1b
|
|
@ -1,25 +0,0 @@
|
||||||
-- Add up migration script here
|
|
||||||
CREATE TABLE "oauth2_state_storage" (
|
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"csrf_state" text NOT NULL,
|
|
||||||
"pkce_code_verifier" text NOT NULL,
|
|
||||||
"return_url" text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "user_sessions" (
|
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"user_id" integer NOT NULL,
|
|
||||||
"session_token_p1" text NOT NULL,
|
|
||||||
"session_token_p2" text NOT NULL,
|
|
||||||
"created_at" integer NOT NULL,
|
|
||||||
"expires_at" integer NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "users" (
|
|
||||||
"id" integer NOT NULL,
|
|
||||||
"email" text NOT NULL UNIQUE,
|
|
||||||
"name" text NOT NULL,
|
|
||||||
"family_name" text NOT NULL,
|
|
||||||
"given_name" text NOT NULL,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
-- Add up migration script here
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS roles (
|
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"description" TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_roles (
|
|
||||||
"user_id" integer NOT NULL,
|
|
||||||
"role_id" integer NOT NULL,
|
|
||||||
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
create TABLE IF NOT EXISTS role_permissions (
|
|
||||||
"role_id" integer NOT NULL,
|
|
||||||
"item" text NOT NULL,
|
|
||||||
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
-- Add up migration script here
|
|
||||||
INSERT INTO "main"."roles" ("id", "created_at", "updated_at", "name", "description") VALUES ('1', '2024-09-27 00:58:54', '2024-09-27 00:58:54', 'public', 'Users with only anonymous access');
|
|
||||||
INSERT INTO "main"."roles" ("id", "created_at", "updated_at", "name", "description") VALUES ('2', '2024-09-27 00:59:24', '2024-09-27 00:59:24', 'normal', 'Users with no elevated privileges');
|
|
||||||
INSERT INTO "main"."roles" ("id", "created_at", "updated_at", "name", "description") VALUES ('3', '2024-09-27 01:00:16', '2024-09-27 01:00:16', 'editor', 'Users with basic elevated privileges');
|
|
||||||
INSERT INTO "main"."roles" ("id", "created_at", "updated_at", "name", "description") VALUES ('4', '2024-09-27 01:00:53', '2024-09-27 01:00:53', 'admin', 'Users with full administrative privileges');
|
|
||||||
|
|
||||||
INSERT INTO "main"."role_permissions" ("role_id", "item", "created_at", "updated_at") VALUES ('4', '/useradmin', '2024-09-27 01:01:59', '2024-09-27 01:01:59');
|
|
||||||
INSERT INTO "main"."role_permissions" ("role_id", "item", "created_at", "updated_at") VALUES ('4', '/users', '2024-09-28 12:14:49', '2024-09-28 12:14:49');
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
-- Add up migration script here
|
||||||
|
CREATE TABLE "oauth2_state_storage" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"csrf_state" text NOT NULL,
|
||||||
|
"pkce_code_verifier" text NOT NULL,
|
||||||
|
"return_url" text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "user_sessions" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user_id" integer NOT NULL,
|
||||||
|
"session_token_p1" text NOT NULL,
|
||||||
|
"session_token_p2" text NOT NULL,
|
||||||
|
"created_at" integer NOT NULL,
|
||||||
|
"expires_at" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"created_at" integer NOT NULL,
|
||||||
|
"created_by" integer NOT NULL,
|
||||||
|
"updated_at" integer NOT NULL,
|
||||||
|
"updated_by" integer NOT NULL,
|
||||||
|
"email" text NOT NULL UNIQUE,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"family_name" text NOT NULL,
|
||||||
|
"given_name" text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"created_at" integer NOT NULL,
|
||||||
|
"created_by" integer NOT NULL,
|
||||||
|
"updated_at" integer NOT NULL,
|
||||||
|
"updated_by" integer NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"created_at" integer NOT NULL,
|
||||||
|
"created_by" integer NOT NULL,
|
||||||
|
"updated_at" integer NOT NULL,
|
||||||
|
"updated_by" integer NOT NULL,
|
||||||
|
"user_id" integer NOT NULL,
|
||||||
|
"role_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
create TABLE IF NOT EXISTS role_permissions (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"created_at" integer NOT NULL,
|
||||||
|
"created_by" integer NOT NULL,
|
||||||
|
"updated_at" integer NOT NULL,
|
||||||
|
"updated_by" integer NOT NULL,
|
||||||
|
"role_id" integer NOT NULL,
|
||||||
|
"item" text NOT NULL
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- Add up migration script here
|
||||||
|
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('1', '0', '0', '0', '0', 'public', 'Users with only anonymous access');
|
||||||
|
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('2', '0', '0', '0', '0', 'normal', 'Users with no elevated privileges');
|
||||||
|
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('3', '0', '0', '0', '0', 'editor', 'Users with basic elevated privileges');
|
||||||
|
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('4', '0', '0', '0', '0', 'admin', 'Users with full administrative privileges');
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
-- Add up migration script here
|
||||||
|
-- Role permissions
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('1', '0', '0', '0', '0', '1', '/');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('2', '0', '0', '0', '0', '1', '/login');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('3', '0', '0', '0', '0', '1', '/logout');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('4', '0', '0', '0', '0', '2', '/dashboard');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('5', '0', '0', '0', '0', '2', '/profile');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('6', '0', '0', '0', '0', '4', '/useradmin');
|
||||||
|
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('7', '0', '0', '0', '0', '4', '/users');
|
||||||
|
|
||||||
|
-- First user is an admin
|
||||||
|
INSERT INTO "main"."user_roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "user_id", "role_id") VALUES ('1', '1728247301', '0', '1728247301', '0', '1', '1');
|
||||||
|
INSERT INTO "main"."user_roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "user_id", "role_id") VALUES ('2', '0', '0', '0', '0', '1', '4');
|
||||||
|
|
@ -70,7 +70,7 @@ pub async fn login(
|
||||||
.remove("return_url")
|
.remove("return_url")
|
||||||
.unwrap_or_else(|| "/".to_string());
|
.unwrap_or_else(|| "/".to_string());
|
||||||
// TODO: check if return_url is valid
|
// TODO: check if return_url is valid
|
||||||
|
|
||||||
let client = get_client(hostname)?;
|
let client = get_client(hostname)?;
|
||||||
|
|
||||||
let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
|
let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||||
|
|
@ -181,11 +181,17 @@ pub async fn google_auth_return(
|
||||||
.bind(email.as_str())
|
.bind(email.as_str())
|
||||||
.fetch_one(&db_pool)
|
.fetch_one(&db_pool)
|
||||||
.await;
|
.await;
|
||||||
let user_id = if let Ok(query) = query
|
let user_id = if let Ok(query) = query {
|
||||||
{
|
|
||||||
query.0
|
query.0
|
||||||
} else {
|
} else {
|
||||||
let query: (i64,) = sqlx::query_as("INSERT INTO users (email, name, family_name, given_name) VALUES (?, ?, ?, ?) RETURNING id")
|
let now = Utc::now().timestamp();
|
||||||
|
|
||||||
|
// Add user
|
||||||
|
let query: (i64,) = sqlx::query_as("INSERT INTO users (created_at, created_by, updated_at, updated_by, email, name, family_name, given_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id")
|
||||||
|
.bind(now)
|
||||||
|
.bind(0 as i64)// Created by system
|
||||||
|
.bind(now)
|
||||||
|
.bind(0 as i64) // Updated by system
|
||||||
.bind(email.clone())
|
.bind(email.clone())
|
||||||
.bind(name.clone())
|
.bind(name.clone())
|
||||||
.bind(family_name.clone())
|
.bind(family_name.clone())
|
||||||
|
|
@ -194,8 +200,12 @@ pub async fn google_auth_return(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Add public role
|
// Add public role
|
||||||
sqlx::query("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)")
|
sqlx::query("INSERT INTO user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) VALUES (?, ?, ?, ?, ?, ?)")
|
||||||
.bind(query.0)
|
.bind(now)
|
||||||
|
.bind(0 as i64)// Created by system
|
||||||
|
.bind(now)
|
||||||
|
.bind(0 as i64) // Updated by system
|
||||||
|
.bind(query.0)
|
||||||
.bind("1")
|
.bind("1")
|
||||||
.execute(&db_pool)
|
.execute(&db_pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -207,7 +217,6 @@ pub async fn google_auth_return(
|
||||||
if let Some(cookie) = cookie {
|
if let Some(cookie) = cookie {
|
||||||
if let Some(_session_token) = cookie.get("session_token") {
|
if let Some(_session_token) = cookie.get("session_token") {
|
||||||
} else {
|
} else {
|
||||||
println!("google_auth_return : No session token");
|
|
||||||
// Create a session for the user
|
// Create a session for the user
|
||||||
let session_token_p1 = Uuid::new_v4().to_string();
|
let session_token_p1 = Uuid::new_v4().to_string();
|
||||||
let session_token_p2 = Uuid::new_v4().to_string();
|
let session_token_p2 = Uuid::new_v4().to_string();
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ use std::net::SocketAddr;
|
||||||
use axum::{
|
use axum::{
|
||||||
middleware, routing::{get, get_service}, Extension, Router
|
middleware, routing::{get, get_service}, Extension, Router
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
|
||||||
use sqlx::{prelude::FromRow, sqlite::SqlitePoolOptions, SqlitePool};
|
|
||||||
use sqlx::migrate::Migrator;
|
use sqlx::migrate::Migrator;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
|
|
@ -11,27 +10,19 @@ mod error_handling;
|
||||||
mod google_oauth;
|
mod google_oauth;
|
||||||
mod middlewares;
|
mod middlewares;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
mod user;
|
||||||
|
|
||||||
use error_handling::AppError;
|
use error_handling::AppError;
|
||||||
use middlewares::inject_user_data;
|
use middlewares::inject_user_data;
|
||||||
use google_oauth::{login, logout, google_auth_return};
|
use google_oauth::{login, logout, google_auth_return};
|
||||||
use routes::{dashboard, index, about, profile, user_profile, useradmin};
|
use routes::{dashboard, index, about, profile, user_profile, useradmin};
|
||||||
|
use user::UserData;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub db_pool: SqlitePool
|
pub db_pool: SqlitePool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)]
|
|
||||||
pub struct UserData {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub id: i64,
|
|
||||||
pub email: String,
|
|
||||||
pub name: String,
|
|
||||||
pub family_name: String,
|
|
||||||
pub given_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// initialize tracing
|
// initialize tracing
|
||||||
|
|
@ -46,10 +37,10 @@ async fn main() {
|
||||||
|
|
||||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||||
|
|
||||||
MIGRATOR
|
/* MIGRATOR
|
||||||
.run(&app_state.db_pool)
|
.run(&app_state.db_pool)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to run migrations");
|
.expect("Failed to run migrations"); */
|
||||||
|
|
||||||
let user_data: Option<UserData> = None;
|
let user_data: Option<UserData> = None;
|
||||||
|
|
||||||
|
|
@ -66,7 +57,6 @@ async fn main() {
|
||||||
.route("/login", get(login))
|
.route("/login", get(login))
|
||||||
.route("/logout", get(logout))
|
.route("/logout", get(logout))
|
||||||
.route("/google_auth_return", get(google_auth_return))
|
.route("/google_auth_return", get(google_auth_return))
|
||||||
//.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), check_auth))
|
|
||||||
.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data))
|
.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data))
|
||||||
.with_state(app_state.db_pool)
|
.with_state(app_state.db_pool)
|
||||||
.layer(Extension(user_data))
|
.layer(Extension(user_data))
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ pub async fn inject_user_data(
|
||||||
|
|
||||||
request.extensions_mut().insert(Some(UserData {
|
request.extensions_mut().insert(Some(UserData {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
|
created_at: row.created_at,
|
||||||
|
created_by: row.created_by,
|
||||||
|
updated_at: row.updated_at,
|
||||||
|
updated_by: row.updated_by,
|
||||||
email: row.email,
|
email: row.email,
|
||||||
name: row.name,
|
name: row.name,
|
||||||
family_name: row.family_name,
|
family_name: row.family_name,
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,23 @@ use axum::{extract::{Path, State}, response::{Html, IntoResponse, Redirect}, Ext
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
use crate::{middlewares::is_authorized, UserData};
|
use crate::{middlewares::is_authorized, user::get_user_roles, UserData};
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "profile.html")]
|
#[template(path = "profile.html")]
|
||||||
struct ProfileTemplate {
|
struct ProfileTemplate {
|
||||||
logged_in: bool,
|
logged_in: bool,
|
||||||
name: String,
|
name: String,
|
||||||
user: UserData
|
user: UserData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "user.html")]
|
||||||
|
struct UserProfileTemplate {
|
||||||
|
logged_in: bool,
|
||||||
|
name: String,
|
||||||
|
user: UserData,
|
||||||
|
user_roles: Vec<crate::user::UserRoles>
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HtmlTemplate<T>(T);
|
struct HtmlTemplate<T>(T);
|
||||||
|
|
@ -87,13 +96,17 @@ pub async fn profile(
|
||||||
let logged_in = user_name.is_some();
|
let logged_in = user_name.is_some();
|
||||||
let name = user_name.unwrap_or_default();
|
let name = user_name.unwrap_or_default();
|
||||||
|
|
||||||
// Extract the user data.
|
if logged_in {
|
||||||
let user = user_data.as_ref().unwrap().clone();
|
// Extract the user data.
|
||||||
|
let user = user_data.as_ref().unwrap().clone();
|
||||||
|
|
||||||
if is_authorized("/profile", user_data, db_pool).await {
|
if is_authorized("/profile", user_data, db_pool).await {
|
||||||
// Create the profile template.
|
// Create the profile template.
|
||||||
let template = ProfileTemplate { logged_in, name, user: user.clone() };
|
let template = ProfileTemplate { logged_in, name, user: user.clone() };
|
||||||
return HtmlTemplate(template).into_response()
|
return HtmlTemplate(template).into_response()
|
||||||
|
} else {
|
||||||
|
Redirect::to("/").into_response()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Redirect::to("/").into_response()
|
Redirect::to("/").into_response()
|
||||||
}
|
}
|
||||||
|
|
@ -119,9 +132,12 @@ pub async fn profile(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if is_authorized("/users", user_data, db_pool).await {
|
if is_authorized("/users", user_data, db_pool.clone()).await {
|
||||||
|
// Get user roles
|
||||||
|
let user_roles = get_user_roles(user_id, &db_pool.clone()).await;
|
||||||
|
|
||||||
// Create the profile template.
|
// Create the profile template.
|
||||||
let template = ProfileTemplate { logged_in, name, user: user };
|
let template = UserProfileTemplate { logged_in, name, user: user, user_roles };
|
||||||
return HtmlTemplate(template).into_response()
|
return HtmlTemplate(template).into_response()
|
||||||
} else {
|
} else {
|
||||||
Redirect::to("/").into_response()
|
Redirect::to("/").into_response()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
///User related structs and functions
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{prelude::FromRow, SqlitePool};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)]
|
||||||
|
pub struct UserData {
|
||||||
|
pub id: i64,
|
||||||
|
pub created_at: i64,
|
||||||
|
pub created_by: i64,
|
||||||
|
pub updated_at: i64,
|
||||||
|
pub updated_by: i64,
|
||||||
|
pub email: String,
|
||||||
|
pub name: String,
|
||||||
|
pub family_name: String,
|
||||||
|
pub given_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct RoleData {
|
||||||
|
pub id: i64,
|
||||||
|
pub created_at: i64,
|
||||||
|
pub created_by: i64,
|
||||||
|
pub updated_at: i64,
|
||||||
|
pub updated_by: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)]
|
||||||
|
pub struct UserRoles {
|
||||||
|
pub id: i64,
|
||||||
|
pub created_at: i64,
|
||||||
|
pub created_by: i64,
|
||||||
|
pub updated_at: i64,
|
||||||
|
pub updated_by: i64,
|
||||||
|
pub user_id: i64,
|
||||||
|
pub role_id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_roles(user_id: i64, db_pool: &SqlitePool) -> Vec<UserRoles> {
|
||||||
|
// Get user roles
|
||||||
|
let user_roles = sqlx::query_as(
|
||||||
|
r#"SELECT id, created_at, created_by, updated_at, updated_by, user_id, role_id FROM user_roles WHERE user_id = ?"#
|
||||||
|
)
|
||||||
|
.bind(user_id)
|
||||||
|
.fetch_all(db_pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
user_roles
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}User Profile{% endblock %}
|
{% block title %}User Profile{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>User Profile</h1>
|
<h1>My Profile</h1>
|
||||||
Full name: {{ user.name }}<br/>
|
Full name: {{ user.name }}<br/>
|
||||||
Given name: {{ user.given_name }}<br/>
|
Given name: {{ user.given_name }}<br/>
|
||||||
Family name: {{ user.family_name }}<br/>
|
Family name: {{ user.family_name }}<br/>
|
||||||
Your email address: {{ user.email }}<br
|
Your email address: {{ user.email }}<br/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends "authorized.html" %}
|
||||||
|
{% block title %}User Profile{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>User Profile</h1>
|
||||||
|
Full name: {{ user.name }}<br/>
|
||||||
|
Given name: {{ user.given_name }}<br/>
|
||||||
|
Family name: {{ user.family_name }}<br/>
|
||||||
|
Your email address: {{ user.email }}<br/>
|
||||||
|
<br/>
|
||||||
|
<h2>User Roles</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user_role in user_roles %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/roles/{{ user_role.id }}">{{ user_role.id }}</a></td>
|
||||||
|
<td>{{ user_role.role_id }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Reference in New Issue