Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: login endpoint and Account rename #7

Merged
merged 4 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@ use validator::ValidationErrors;
use crate::error::HttpStatusCode;

#[derive(Debug, Error)]
pub enum UserErrorCode {
pub enum AccountErrorCode {
#[error("Vaildation error. {0}")]
ValidationError(#[from] ValidationErrors),
#[error("The username {0} is already taken")]
UsernameTaken(String),
}

impl HttpStatusCode for UserErrorCode {
impl HttpStatusCode for AccountErrorCode {
fn status_code(&self) -> StatusCode {
match self {
UserErrorCode::ValidationError(_) => StatusCode::BAD_REQUEST,
UserErrorCode::UsernameTaken(_) => StatusCode::CONFLICT,
AccountErrorCode::ValidationError(_) => StatusCode::BAD_REQUEST,
AccountErrorCode::UsernameTaken(_) => StatusCode::CONFLICT,
}
}

fn error_code(&self) -> &'static str {
match self {
UserErrorCode::ValidationError(_) => "VALIDATION_ERROR",
UserErrorCode::UsernameTaken(_) => "USERNAME_TAKEN",
AccountErrorCode::ValidationError(_) => "VALIDATION_ERROR",
AccountErrorCode::UsernameTaken(_) => "USERNAME_TAKEN",
}
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[derive(Debug, Clone)]
pub struct User {
pub struct Account {
pub username: String,
pub email: String,
pub session: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::Arc;

use tracing::instrument;
use url::Url;
use validator::{Validate, ValidationError};
Expand All @@ -6,25 +8,20 @@ use matrix::admin::resources::user::{
ListUsersParams, ThreePid, User as MatrixUser, UserCreateDto,
};
use matrix::admin::resources::user_id::UserId;
use matrix::admin::Client as MatrixAdminClient;
use matrix::Client as MatrixAdminClient;

use crate::util::secret::Secret;
use crate::util::time::timestamp;
use crate::{Error, Result};

use super::error::UserErrorCode;
use super::model::User;
use super::error::AccountErrorCode;
use super::model::Account;

const DEFAULT_AVATAR_URL: &str = "https://via.placeholder.com/150";
const MIN_USERNAME_LENGTH: usize = 3;
const MAX_USERNAME_LENGTH: usize = 12;
const MIN_PASSWORD_LENGTH: usize = 8;

pub struct LoginDto {
pub username: String,
pub password: String,
}

#[derive(Debug, Validate)]
pub struct CreateAccountDto {
#[validate(custom = "CreateAccountDto::validate_username")]
Expand Down Expand Up @@ -71,20 +68,20 @@ impl CreateAccountDto {
}
}

pub struct UserService {
admin: MatrixAdminClient,
pub struct AccountService {
admin: Arc<MatrixAdminClient>,
}

impl UserService {
pub fn new(admin: MatrixAdminClient) -> Self {
impl AccountService {
pub fn new(admin: Arc<MatrixAdminClient>) -> Self {
Self { admin }
}

#[instrument(skip(self, dto))]
pub async fn register(&self, dto: CreateAccountDto) -> Result<User> {
pub async fn register(&self, dto: CreateAccountDto) -> Result<Account> {
dto.validate().map_err(|err| {
tracing::warn!(?err, "Failed to validate user creation dto");
UserErrorCode::from(err)
AccountErrorCode::from(err)
})?;

let user_id = UserId::new(dto.username.clone(), self.admin.server_name().to_string());
Expand All @@ -102,7 +99,7 @@ impl UserService {
})?;

if !exists.users.is_empty() {
return Err(UserErrorCode::UsernameTaken(dto.username).into());
return Err(AccountErrorCode::UsernameTaken(dto.username).into());
}

let avatar_url = Url::parse(DEFAULT_AVATAR_URL).map_err(|err| {
Expand Down Expand Up @@ -147,7 +144,7 @@ impl UserService {
return Err(Error::Unknown);
};

Ok(User {
Ok(Account {
username: displayname,
email: threepid.address.to_owned(),
session: dto.session,
Expand Down
24 changes: 24 additions & 0 deletions crates/core/src/auth/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use http::StatusCode;
use thiserror::Error;

use crate::error::HttpStatusCode;

#[derive(Debug, Error)]
pub enum AuthErrorCode {
#[error("Provided credentials are not valid")]
InvalidCredentials,
}

impl HttpStatusCode for AuthErrorCode {
fn status_code(&self) -> StatusCode {
match self {
AuthErrorCode::InvalidCredentials => StatusCode::BAD_REQUEST,
}
}

fn error_code(&self) -> &'static str {
match self {
AuthErrorCode::InvalidCredentials => "INVALID_CREDENTIALS",
}
}
}
2 changes: 2 additions & 0 deletions crates/core/src/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod error;
pub mod service;
40 changes: 40 additions & 0 deletions crates/core/src/auth/service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::sync::Arc;

use matrix::client::resources::login::Login;
use matrix::Client as MatrixAdminClient;

use crate::util::secret::Secret;
use crate::Result;

pub struct LoginCredentials {
pub username: String,
pub password: Secret,
}

pub struct LoginCredentialsResponse {
pub access_token: Secret,
}

pub struct AuthService {
admin: Arc<MatrixAdminClient>,
}

impl AuthService {
pub fn new(admin: Arc<MatrixAdminClient>) -> Self {
Self { admin }
}

pub async fn login(&self, credentials: LoginCredentials) -> Result<LoginCredentialsResponse> {
let login_response = Login::login_credentials(
&self.admin,
credentials.username,
credentials.password.inner(),
)
.await
.unwrap();

Ok(LoginCredentialsResponse {
access_token: Secret::new(login_response.access_token),
})
}
}
8 changes: 4 additions & 4 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use http::StatusCode;
use thiserror::Error;

use crate::user::error::UserErrorCode;
use crate::account::error::AccountErrorCode;

pub type Result<T> = std::result::Result<T, Error>;

Expand All @@ -13,13 +13,13 @@ pub trait HttpStatusCode {
#[derive(Debug, Error)]
pub enum Error {
#[error("User Error. {0}")]
User(UserErrorCode),
User(AccountErrorCode),
#[error("Unknown Error Occured")]
Unknown,
}

impl From<UserErrorCode> for Error {
fn from(err: UserErrorCode) -> Self {
impl From<AccountErrorCode> for Error {
fn from(err: AccountErrorCode) -> Self {
Error::User(err)
}
}
Expand Down
34 changes: 22 additions & 12 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
pub mod account;
pub mod auth;
pub mod error;
pub mod user;
pub mod util;

pub use error::{Error, HttpStatusCode, Result};

use std::fmt::Debug;
use std::sync::Arc;

use matrix::admin::Client as MatrixAdminClient;
use matrix::Client as MatrixAdminClient;

use self::user::service::UserService;
use self::account::service::AccountService;
use self::auth::service::AuthService;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CommuneConfig {
Expand All @@ -19,25 +22,32 @@ pub struct CommuneConfig {
}

pub struct Commune {
pub user: UserService,
pub account: Arc<AccountService>,
pub auth: Arc<AuthService>,
}

impl Commune {
pub fn new<C: Into<CommuneConfig>>(config: C) -> Result<Self> {
pub async fn new<C: Into<CommuneConfig>>(config: C) -> Result<Self> {
let config: CommuneConfig = config.into();
let mut admin = MatrixAdminClient::new(config.synapse_host, config.synapse_server_name)
let mut admin = MatrixAdminClient::new(&config.synapse_host, &config.synapse_server_name)
.map_err(|err| {
tracing::error!(?err, "Failed to create admin client");
tracing::error!(?err, "Failed to create admin client");
Error::Unknown
})?;

admin
.set_token(&config.synapse_admin_token)
.map_err(|err| {
tracing::error!(?err, "Failed to set admin token");
Error::Unknown
})?;

admin.set_token(config.synapse_admin_token).map_err(|err| {
tracing::error!(?err, "Failed to set admin token");
Error::Unknown
})?;
let admin_client = Arc::new(admin);
let auth = AuthService::new(Arc::clone(&admin_client));

Ok(Self {
user: UserService::new(admin),
account: Arc::new(AccountService::new(Arc::clone(&admin_client))),
auth: Arc::new(auth),
})
}
}
1 change: 0 additions & 1 deletion crates/matrix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ publish = false
async-trait = "0.1.74"
hex = "0.4.3"
hmac = "0.12.1"
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", rev = "e43a25a" }
serde_path_to_error = "0.1.14"
serde_qs = "0.12.0"
sha1 = "0.10.6"
Expand Down
3 changes: 0 additions & 3 deletions crates/matrix/src/admin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
pub mod client;
pub mod resources;

pub use client::Client;
2 changes: 1 addition & 1 deletion crates/matrix/src/admin/resources/token/shared_secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
use sha1::Sha1;

use crate::admin::Client;
use crate::http::Client;

type HmacSha1 = Hmac<Sha1>;

Expand Down
3 changes: 1 addition & 2 deletions crates/matrix/src/admin/resources/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use tracing::instrument;
use url::Url;

use crate::admin::Client;
use crate::http::Client;

use super::user_id::UserId;

Expand Down Expand Up @@ -141,7 +141,6 @@ impl User {
let resp = client
.get_query("/_synapse/admin/v2/users", &params)
.await?;
println!("{:?}", resp);
let data: ListUsersResponse = resp.json().await?;

Ok(data)
Expand Down
1 change: 1 addition & 0 deletions crates/matrix/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod resources;
48 changes: 48 additions & 0 deletions crates/matrix/src/client/resources/login.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use tracing::instrument;

use crate::http::Client;

#[derive(Debug, Serialize, Deserialize)]
pub struct LoginCredentials {
pub access_token: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct LoginCredentialsPayload {
pub r#type: &'static str,
pub user: String,
pub password: String,
}

pub struct Login;

impl Login {
/// Retrieves an access token by logging in with Username and Password
///
/// This is equivalent to executing:
///
/// ```ignore
/// curl -sS -d '{"type":"m.login.password", "user":"X", "password":"Y"}' http://server:port/_matrix/client/v3/login
/// ```
#[instrument(skip(client, username, password))]
pub async fn login_credentials(
client: &Client,
username: impl AsRef<str>,
password: impl AsRef<str>,
) -> Result<LoginCredentials> {
let resp = client
.post_json(
"/_matrix/client/v3/login",
&LoginCredentialsPayload {
r#type: "m.login.password",
user: username.as_ref().to_string(),
password: password.as_ref().to_string(),
},
)
.await?;

Ok(resp.json().await?)
}
}
1 change: 1 addition & 0 deletions crates/matrix/src/client/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod login;
File renamed without changes.
16 changes: 8 additions & 8 deletions crates/matrix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
//!
//! Reexports `matrix_sdk` and provides implementations on Matrix Admin API.

mod http;

pub use http::Client;

/// Implementation on the Administrator API of Matrix
///
/// Refer: https://matrix-org.github.io/synapse/latest/usage/administration/index.html
pub mod admin;

/// The official Matrix Rust SDK.
///
/// # Project State
///
/// As of today this SDK is still in beta and is not yet ready for production,
/// so we make use of the repo at a specific commit.
/// Implementation on the Client API of Matrix
///
/// Refer: https://github.com/matrix-org/matrix-rust-sdk
pub use matrix_sdk as sdk;
/// Different to the Matrix SDK, no user state is kept in the Client instance,
/// this is equivalent to making cURL requests to the Matrix server.
pub mod client;
Loading
Loading