Merge pull request #17 from fLotte-meets-HWR-DB/develop

Users now have attributes to store custom data
leon_tries_rust
Trivernis 4 years ago committed by GitHub
commit 8ef4e744bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
Cargo.lock generated

@ -1151,6 +1151,8 @@ dependencies = [
"bytes",
"fallible-iterator",
"postgres-protocol",
"serde",
"serde_json",
]
[[package]]

@ -9,7 +9,7 @@ license = "GPL-3.0"
[dependencies]
msgrpc = "0.1.0"
postgres = "0.17.5"
postgres = {version = "0.17.5", features = ["with-serde_json-1"]}
serde_postgres = "0.2.0"
dotenv = "0.15.0"
serde = { version = "1.0.115", features = ["serde_derive"] }

@ -14,6 +14,7 @@ use crate::database::roles::Roles;
use crate::database::user_roles::UserRoles;
use crate::database::users::Users;
use crate::utils::error::DatabaseResult;
use serde_json::Value;
pub mod models;
pub mod permissions;
@ -80,8 +81,9 @@ impl Database {
"ADMIN".to_string(),
dotenv::var(ENV_ADMIN_EMAIL).unwrap_or(DEFAULT_ADMIN_EMAIL.to_string()),
dotenv::var(ENV_ADMIN_PASSWORD).unwrap_or(DEFAULT_ADMIN_PASSWORD.to_string()),
Value::Null,
) {
log::debug!("Failed to create admin user {}", e);
log::debug!("Failed to create admin user: {}", e);
} else {
log::debug!("Admin user created successfully!");
}

@ -4,27 +4,28 @@
use postgres::Row;
use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use serde_json::Value;
/// Record to store data in when retrieving rows from the users table
#[derive(Clone, Debug, Zeroize, Serialize)]
#[zeroize(drop)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserRecord {
pub id: i32,
pub name: String,
pub email: String,
pub password_hash: Vec<u8>,
pub salt: Vec<u8>,
pub attributes: serde_json::Value,
}
impl UserRecord {
pub fn from_ordered_row(row: &Row) -> Self {
pub fn from_row(row: Row) -> Self {
Self {
id: row.get(0),
name: row.get(1),
email: row.get(2),
password_hash: row.get(3),
salt: row.get(4),
id: row.get("id"),
name: row.get("name"),
email: row.get("email"),
password_hash: row.get("password_hash"),
salt: row.get("salt"),
attributes: row.get("attributes"),
}
}
}
@ -62,6 +63,18 @@ pub struct UserInformation {
pub id: i32,
pub name: String,
pub email: String,
pub attributes: Value,
}
impl UserInformation {
pub fn from_row(row: Row) -> Self {
Self {
id: row.get("id"),
name: row.get("name"),
email: row.get("email"),
attributes: row.get("attributes"),
}
}
}
#[derive(Serialize, Deserialize, JsonSchema)]
@ -69,15 +82,17 @@ pub struct UserFullInformation {
pub id: i32,
pub name: String,
pub email: String,
pub attributes: Value,
pub roles: Vec<Role>,
}
impl From<UserRecord> for UserInformation {
fn from(record: UserRecord) -> Self {
Self {
id: record.id.clone(),
name: record.name.clone(),
email: record.email.clone(),
id: record.id,
name: record.name,
email: record.email,
attributes: record.attributes,
}
}
}

@ -13,6 +13,7 @@ use crate::database::user_roles::UserRoles;
use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError;
use crate::utils::{create_salt, hash_password};
use serde_json::Value;
/// Table that stores users with their email addresses and hashed passwords
#[derive(Clone)]
@ -38,7 +39,8 @@ impl Table for Users {
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash BYTEA NOT NULL,
salt BYTEA NOT NULL
salt BYTEA NOT NULL,
attributes JSONB NOT NULL DEFAULT '{}'
);",
)?;
@ -55,6 +57,7 @@ impl Users {
name: String,
email: String,
password: String,
attributes: Value,
) -> DatabaseResult<UserRecord> {
let mut connection = self.pool.get()?;
let mut password = Zeroizing::new(password);
@ -72,10 +75,10 @@ impl Users {
hash_password(password.as_bytes(), &*salt).map_err(|e| DBError::GenericError(e))?;
password.zeroize();
let row = connection.query_one("
INSERT INTO users (name, email, password_hash, salt) VALUES ($1, $2, $3, $4) RETURNING *;
", &[&name, &email, &pw_hash.to_vec(), &salt.to_vec()])?;
INSERT INTO users (name, email, password_hash, salt, attributes) VALUES ($1, $2, $3, $4, $5) RETURNING *;
", &[&name, &email, &pw_hash.to_vec(), &salt.to_vec(), &attributes])?;
Ok(UserRecord::from_ordered_row(&row))
Ok(UserRecord::from_row(row))
}
pub fn update_user(
@ -83,13 +86,15 @@ impl Users {
old_email: &String,
name: &String,
email: &String,
attributes: &Value,
password: &Option<String>,
) -> DatabaseResult<UserInformation> {
log::trace!(
"Updating user {} with new entries name: {}, email: {}",
"Updating user {} with new entries name: {}, email: {}, attributes: {:?}",
old_email,
name,
email
email,
attributes,
);
let mut connection = self.pool.get()?;
if connection
@ -115,17 +120,17 @@ impl Users {
let pw_hash =
hash_password(password.as_bytes(), &*salt).map_err(|e| DBError::GenericError(e))?;
connection.query_one(
"UPDATE users SET name = $1, email = $2, password_hash = $3, salt = $4 WHERE email = $5 RETURNING *",
&[&name, &email, &pw_hash.to_vec(), &salt.to_vec(), &old_email],
"UPDATE users SET name = $1, email = $2, password_hash = $3, salt = $4, attributes = $5 WHERE email = $6 RETURNING *",
&[&name, &email, &pw_hash.to_vec(), &salt.to_vec(), &attributes, &old_email],
)?
} else {
connection.query_one(
"UPDATE users SET name = $1, email = $2 WHERE email = $3 RETURNING *",
&[&name, &email, &old_email],
"UPDATE users SET name = $1, email = $2, attributes = $3 WHERE email = $4 RETURNING *",
&[&name, &email, &attributes, &old_email],
)?
};
Ok(serde_postgres::from_row::<UserInformation>(&new_record)?)
Ok(UserInformation::from_row(new_record))
}
/// Returns information about a user by Id
@ -133,10 +138,13 @@ impl Users {
log::trace!("Looking up entry for user with id {}", id);
let mut connection = self.pool.get()?;
let result = connection
.query_opt("SELECT id, name, email FROM users WHERE id = $1", &[&id])?
.query_opt(
"SELECT id, name, email, attributes FROM users WHERE id = $1",
&[&id],
)?
.ok_or(DBError::RecordDoesNotExist)?;
Ok(serde_postgres::from_row::<UserInformation>(&result)?)
Ok(UserInformation::from_row(result))
}
pub fn get_user_by_email(&self, email: &String) -> DatabaseResult<UserInformation> {
@ -144,22 +152,22 @@ impl Users {
let mut connection = self.pool.get()?;
let result = connection
.query_opt(
"SELECT id, name, email FROM users WHERE email = $1",
"SELECT id, name, email, attributes FROM users WHERE email = $1",
&[email],
)?
.ok_or(DBError::RecordDoesNotExist)?;
Ok(serde_postgres::from_row::<UserInformation>(&result)?)
Ok(UserInformation::from_row(result))
}
pub fn get_users(&self) -> DatabaseResult<Vec<UserInformation>> {
log::trace!("Returning a list of all users...");
let mut connection = self.pool.get()?;
let results = connection.query("SELECT id, name, email FROM users", &[])?;
let results = connection.query("SELECT id, name, email, attributes FROM users", &[])?;
let mut users = Vec::new();
for result in results {
users.push(serde_postgres::from_row::<UserInformation>(&result)?);
users.push(UserInformation::from_row(result));
}
Ok(users)

@ -21,8 +21,8 @@ use crate::database::Database;
use crate::server::documentation::RESTDocumentation;
use crate::server::messages::{
CreateUserRequest, DeleteRoleResponse, DeleteUserRequest, DeleteUserResponse, ErrorMessage,
FullRoleData, LoginMessage, LogoutConfirmation, LogoutMessage, ModifyRoleRequest,
RefreshMessage, UpdateUserRequest,
FullRoleData, LoginRequest, LoginResponse, LogoutConfirmation, LogoutMessage,
ModifyRoleRequest, RefreshMessage, UpdateUserRequest,
};
use crate::utils::error::DBError;
use crate::utils::get_user_id_from_token;
@ -181,7 +181,7 @@ impl UserHttpServer {
fn build_docs() -> Result<RESTDocumentation, serde_json::Error> {
let mut doc = RESTDocumentation::new("/info");
doc.add_path::<LoginMessage, SessionTokens>(
doc.add_path::<LoginRequest, LoginResponse>(
"/login",
"POST",
"Returns request and refresh tokens",
@ -261,15 +261,25 @@ impl UserHttpServer {
/// Handles the login part of the REST api
fn login(database: &Database, request: &Request) -> HTTPResult<Response> {
let login_request: LoginMessage =
let login_request: LoginRequest =
serde_json::from_str(parse_string_body(request)?.as_str())
.map_err(|e| HTTPError::new(e.to_string(), 400))?;
let tokens = database
.users
.create_tokens(&login_request.email, &login_request.password)?;
Ok(Response::json(&tokens).with_status_code(201))
let user = database
.users
.get_user(get_user_id_from_token(&tokens.request_token).unwrap())?;
Ok(Response::json(&LoginResponse {
request_token: tokens.request_token.clone(),
refresh_token: tokens.refresh_token.clone(),
request_ttl: tokens.request_ttl,
refresh_ttl: tokens.refresh_ttl,
user,
})
.with_status_code(201))
}
/// Handles the new token part of the rest api
@ -391,6 +401,7 @@ impl UserHttpServer {
id: user.id,
name: user.name,
email: user.email,
attributes: user.attributes,
roles,
}))
}
@ -411,6 +422,7 @@ impl UserHttpServer {
message.name.clone(),
message.email.clone(),
message.password.clone(),
message.attributes.clone(),
)?;
Ok(Response::json(&UserInformation::from(result)).with_status_code(201))
@ -437,6 +449,7 @@ impl UserHttpServer {
&email,
&message.name.clone().unwrap_or(user_record.name),
&message.email.clone().unwrap_or(user_record.email),
&message.attributes.clone().unwrap_or(user_record.attributes),
&message.password,
)?;
let roles = if let Some(roles) = &message.roles {
@ -450,6 +463,7 @@ impl UserHttpServer {
id: record.id,
email: record.email,
name: record.name,
attributes: record.attributes,
roles,
}))
}

@ -10,8 +10,9 @@ use serde::export::Formatter;
use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use crate::database::models::{CreatePermissionsEntry, Permission};
use crate::database::models::{CreatePermissionsEntry, Permission, UserInformation};
use crate::utils::error::DBError;
use serde_json::Value;
#[derive(Deserialize)]
pub struct TokenRequest {
@ -85,11 +86,20 @@ pub struct CreatePermissionsRequest {
#[derive(Deserialize, Zeroize, JsonSchema)]
#[zeroize(drop)]
pub struct LoginMessage {
pub struct LoginRequest {
pub email: String,
pub password: String,
}
#[derive(Deserialize, Serialize, JsonSchema)]
pub struct LoginResponse {
pub request_token: String,
pub refresh_token: String,
pub request_ttl: i32,
pub refresh_ttl: i32,
pub user: UserInformation,
}
#[derive(Deserialize, Zeroize, JsonSchema)]
#[zeroize(drop)]
pub struct RefreshMessage {
@ -120,22 +130,22 @@ pub struct DeleteRoleResponse {
pub role: String,
}
#[derive(Deserialize, JsonSchema, Zeroize)]
#[zeroize(drop)]
#[derive(Deserialize, JsonSchema)]
pub struct UpdateUserRequest {
pub name: Option<String>,
pub email: Option<String>,
pub password: Option<String>,
pub roles: Option<Vec<String>>,
pub attributes: Option<Value>,
pub own_password: String,
}
#[derive(Deserialize, JsonSchema, Zeroize)]
#[zeroize(drop)]
#[derive(Deserialize, JsonSchema)]
pub struct CreateUserRequest {
pub name: String,
pub email: String,
pub password: String,
pub attributes: Value,
}
#[derive(Deserialize, JsonSchema, Zeroize)]

Loading…
Cancel
Save