using unified system
This commit is contained in:
304
komp-app/src/auth.rs
Normal file
304
komp-app/src/auth.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use anyhow::{Context, Result};
|
||||
use common::proto::komp_ac::auth::{
|
||||
AddRoleRequest, AssignUserRoleRequest, AuthResponse, AuthorizationSnapshot,
|
||||
ChangePasswordRequest, GetAuthorizationRequest, GrantPermissionRequest,
|
||||
GrantableObject, ListGrantableObjectsRequest, ListRolePermissionsRequest, ListRolesRequest,
|
||||
ListUsersRequest, LoginRequest, LoginResponse, LogoutRequest, PasswordOperationResponse,
|
||||
RegisterRequest, RemoveRoleRequest, ResetUserPasswordRequest, RevokePermissionRequest,
|
||||
RevokeUserSessionsRequest, Role, RolePermissions, UserSummary,
|
||||
auth_service_client::AuthServiceClient,
|
||||
};
|
||||
use tonic::transport::Channel;
|
||||
use tonic::Request;
|
||||
|
||||
use crate::transport::{DEFAULT_GRPC_ENDPOINT, authenticated_request, connect_channel};
|
||||
|
||||
pub fn validate_password_change(password: &str, confirmation: &str) -> Result<()> {
|
||||
if password.is_empty() {
|
||||
anyhow::bail!("Password is required");
|
||||
}
|
||||
if password != confirmation {
|
||||
anyhow::bail!("Passwords do not match");
|
||||
}
|
||||
if password.len() < 8 {
|
||||
anyhow::bail!("Password must be at least 8 characters");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_optional_password(password: Option<&str>, confirmation: Option<&str>) -> Result<()> {
|
||||
let password = password.unwrap_or_default();
|
||||
let confirmation = confirmation.unwrap_or_default();
|
||||
if password.is_empty() && confirmation.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
validate_password_change(password, confirmation)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AuthClient {
|
||||
client: AuthServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl AuthClient {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let endpoint = std::env::var("GRPC_ENDPOINT")
|
||||
.unwrap_or_else(|_| DEFAULT_GRPC_ENDPOINT.to_string());
|
||||
Self::connect(&endpoint).await
|
||||
}
|
||||
|
||||
pub async fn connect(endpoint: &str) -> Result<Self> {
|
||||
Self::with_channel(connect_channel(endpoint).await?).await
|
||||
}
|
||||
|
||||
pub async fn with_channel(channel: Channel) -> Result<Self> {
|
||||
Ok(Self {
|
||||
client: AuthServiceClient::new(channel),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn login(&mut self, identifier: String, password: String) -> Result<LoginResponse> {
|
||||
Ok(self
|
||||
.client
|
||||
.login(Request::new(LoginRequest {
|
||||
identifier,
|
||||
password,
|
||||
}))
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
pub async fn register(
|
||||
&mut self,
|
||||
username: String,
|
||||
email: String,
|
||||
password: Option<String>,
|
||||
password_confirmation: Option<String>,
|
||||
timezone: String,
|
||||
phone_country: String,
|
||||
) -> Result<AuthResponse> {
|
||||
validate_optional_password(password.as_deref(), password_confirmation.as_deref())?;
|
||||
Ok(self
|
||||
.client
|
||||
.register(Request::new(RegisterRequest {
|
||||
username,
|
||||
email,
|
||||
password: password.unwrap_or_default(),
|
||||
password_confirmation: password_confirmation.unwrap_or_default(),
|
||||
timezone,
|
||||
phone_country,
|
||||
}))
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
pub async fn change_password(
|
||||
&mut self,
|
||||
token: &str,
|
||||
current_password: String,
|
||||
new_password: String,
|
||||
new_password_confirmation: String,
|
||||
) -> Result<PasswordOperationResponse> {
|
||||
validate_password_change(&new_password, &new_password_confirmation)?;
|
||||
let request = authenticated_request(
|
||||
Some(token),
|
||||
ChangePasswordRequest {
|
||||
current_password,
|
||||
new_password,
|
||||
new_password_confirmation,
|
||||
},
|
||||
)?;
|
||||
Ok(self.client.change_password(request).await?.into_inner())
|
||||
}
|
||||
|
||||
pub async fn logout(&mut self, token: &str) -> Result<()> {
|
||||
self.client
|
||||
.logout(authenticated_request(Some(token), LogoutRequest {})?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_authorization(&mut self, token: &str) -> Result<AuthorizationSnapshot> {
|
||||
Ok(self
|
||||
.client
|
||||
.get_authorization(authenticated_request(Some(token), GetAuthorizationRequest {})?)
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
pub async fn list_roles(&mut self, token: &str) -> Result<Vec<Role>> {
|
||||
Ok(self
|
||||
.client
|
||||
.list_roles(authenticated_request(Some(token), ListRolesRequest {})?)
|
||||
.await?
|
||||
.into_inner()
|
||||
.roles)
|
||||
}
|
||||
|
||||
pub async fn add_role(&mut self, token: &str, name: String, parent: String) -> Result<()> {
|
||||
self.client
|
||||
.add_role(authenticated_request(
|
||||
Some(token),
|
||||
AddRoleRequest { name, parent },
|
||||
)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_role(&mut self, token: &str, name: String) -> Result<()> {
|
||||
self.client
|
||||
.remove_role(authenticated_request(Some(token), RemoveRoleRequest { name })?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn grant_permission(
|
||||
&mut self,
|
||||
token: &str,
|
||||
role: String,
|
||||
object: String,
|
||||
action: String,
|
||||
) -> Result<()> {
|
||||
self.client
|
||||
.grant_permission(authenticated_request(
|
||||
Some(token),
|
||||
GrantPermissionRequest {
|
||||
role,
|
||||
object,
|
||||
action,
|
||||
},
|
||||
)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn revoke_permission(
|
||||
&mut self,
|
||||
token: &str,
|
||||
role: String,
|
||||
object: String,
|
||||
action: String,
|
||||
) -> Result<()> {
|
||||
self.client
|
||||
.revoke_permission(authenticated_request(
|
||||
Some(token),
|
||||
RevokePermissionRequest {
|
||||
role,
|
||||
object,
|
||||
action,
|
||||
},
|
||||
)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn try_revoke_permission(
|
||||
&mut self,
|
||||
token: &str,
|
||||
role: String,
|
||||
object: String,
|
||||
action: String,
|
||||
) -> Result<bool> {
|
||||
let request = authenticated_request(
|
||||
Some(token),
|
||||
RevokePermissionRequest {
|
||||
role,
|
||||
object,
|
||||
action,
|
||||
},
|
||||
)?;
|
||||
match self.client.revoke_permission(request).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(status) if status.code() == tonic::Code::NotFound => Ok(false),
|
||||
Err(status) => Err(status.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_role_permissions(
|
||||
&mut self,
|
||||
token: &str,
|
||||
role: String,
|
||||
) -> Result<RolePermissions> {
|
||||
Ok(self
|
||||
.client
|
||||
.list_role_permissions(authenticated_request(
|
||||
Some(token),
|
||||
ListRolePermissionsRequest { role },
|
||||
)?)
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
pub async fn list_grantable_objects(
|
||||
&mut self,
|
||||
token: &str,
|
||||
target_role: String,
|
||||
) -> Result<Vec<GrantableObject>> {
|
||||
Ok(self
|
||||
.client
|
||||
.list_grantable_objects(authenticated_request(
|
||||
Some(token),
|
||||
ListGrantableObjectsRequest { target_role },
|
||||
)?)
|
||||
.await?
|
||||
.into_inner()
|
||||
.objects)
|
||||
}
|
||||
|
||||
pub async fn assign_user_role(
|
||||
&mut self,
|
||||
token: &str,
|
||||
username: String,
|
||||
role: String,
|
||||
) -> Result<()> {
|
||||
self.client
|
||||
.assign_user_role(authenticated_request(
|
||||
Some(token),
|
||||
AssignUserRoleRequest { username, role },
|
||||
)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_users(&mut self, token: &str) -> Result<Vec<UserSummary>> {
|
||||
Ok(self
|
||||
.client
|
||||
.list_users(authenticated_request(Some(token), ListUsersRequest {})?)
|
||||
.await?
|
||||
.into_inner()
|
||||
.users)
|
||||
}
|
||||
|
||||
pub async fn revoke_user_sessions(&mut self, token: &str, username: String) -> Result<()> {
|
||||
self.client
|
||||
.revoke_user_sessions(authenticated_request(
|
||||
Some(token),
|
||||
RevokeUserSessionsRequest { username },
|
||||
)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reset_user_password(
|
||||
&mut self,
|
||||
token: &str,
|
||||
username: String,
|
||||
new_password: String,
|
||||
new_password_confirmation: String,
|
||||
) -> Result<PasswordOperationResponse> {
|
||||
validate_password_change(&new_password, &new_password_confirmation)?;
|
||||
Ok(self
|
||||
.client
|
||||
.reset_user_password(authenticated_request(
|
||||
Some(token),
|
||||
ResetUserPasswordRequest {
|
||||
username,
|
||||
new_password,
|
||||
new_password_confirmation,
|
||||
},
|
||||
)?)
|
||||
.await
|
||||
.context("Failed to reset user password")?
|
||||
.into_inner())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user