blob: 875dcd2786986fcc68bc934b40fd13ce105c2e40 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::error::AuthenticationError;
use crate::error::AuthenticationServiceError;
use crate::user_db::DbClient;
use crate::user_info::UserInfo;
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[allow(unused_imports)]
use std::untrusted::time::SystemTimeEx;
use teaclave_proto::teaclave_authentication_service::*;
use teaclave_rpc::{Request, Response};
use teaclave_service_enclave_utils::{bail, ensure};
use teaclave_types::{TeaclaveServiceResponseResult, UserRole};
#[derive(Clone)]
pub(crate) struct TeaclaveAuthenticationApiService {
db_client: Arc<Mutex<DbClient>>,
jwt_secret: Vec<u8>,
}
impl TeaclaveAuthenticationApiService {
pub(crate) fn new(db_client: DbClient, jwt_secret: Vec<u8>) -> Self {
Self {
db_client: Arc::new(Mutex::new(db_client)),
jwt_secret,
}
}
fn validate_user_credential(
&self,
id: &str,
token: &str,
) -> Result<UserRole, AuthenticationError> {
let user: UserInfo = match self.db_client.lock().unwrap().get_user(id) {
Ok(value) => value,
Err(_) => bail!(AuthenticationError::InvalidUserId),
};
if token.is_empty() {
bail!(AuthenticationError::InvalidToken);
}
match user.validate_token(&self.jwt_secret, token) {
Ok(claims) => Ok(claims.get_role()),
Err(_) => bail!(AuthenticationError::IncorrectToken),
}
}
fn validate_credential_in_request<T>(
&self,
request: &Request<T>,
) -> Result<UserRole, AuthenticationServiceError> {
let id: String = request
.metadata()
.get("id")
.and_then(|x| x.to_str().ok())
.ok_or(AuthenticationServiceError::MissingUserId)?
.into();
let token: String = request
.metadata()
.get("token")
.and_then(|x| x.to_str().ok())
.ok_or(AuthenticationServiceError::MissingToken)?
.into();
let requester_role = self.validate_user_credential(&id, &token)?;
Ok(requester_role)
}
}
#[teaclave_rpc::async_trait]
impl TeaclaveAuthenticationApi for TeaclaveAuthenticationApiService {
async fn user_register(
&self,
request: Request<UserRegisterRequest>,
) -> TeaclaveServiceResponseResult<()> {
let requester_role = self.validate_credential_in_request(&request)?;
let request = request.get_ref();
ensure!(
!request.id.is_empty(),
AuthenticationServiceError::InvalidUserId
);
if self.db_client.lock().unwrap().get_user(&request.id).is_ok() {
bail!(AuthenticationServiceError::UserIdExist);
}
let role = UserRole::new(&request.role, &request.attribute);
ensure!(
role != UserRole::Invalid,
AuthenticationServiceError::InvalidRole
);
ensure!(
authorize_user_register(&requester_role, request),
AuthenticationServiceError::PermissionDenied
);
let new_user = UserInfo::new(&request.id, &request.password, role);
match self.db_client.lock().unwrap().create_user(&new_user) {
Ok(_) => Ok(Response::new(())),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
async fn user_update(
&self,
request: Request<UserUpdateRequest>,
) -> TeaclaveServiceResponseResult<()> {
let requester_role = self.validate_credential_in_request(&request)?;
let request = request.get_ref();
ensure!(
!request.id.is_empty(),
AuthenticationServiceError::InvalidUserId
);
if self
.db_client
.lock()
.unwrap()
.get_user(&request.id)
.is_err()
{
bail!(AuthenticationServiceError::InvalidUserId);
}
let role = UserRole::new(&request.role, &request.attribute);
ensure!(
role != UserRole::Invalid,
AuthenticationServiceError::InvalidRole
);
ensure!(
authorize_user_update(&requester_role, request),
AuthenticationServiceError::PermissionDenied
);
let updated_user = UserInfo::new(&request.id, &request.password, role);
match self.db_client.lock().unwrap().update_user(&updated_user) {
Ok(_) => Ok(Response::new(())),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
async fn user_login(
&self,
request: Request<UserLoginRequest>,
) -> TeaclaveServiceResponseResult<UserLoginResponse> {
let request = request.get_ref();
ensure!(!request.id.is_empty(), AuthenticationError::InvalidUserId);
ensure!(
!request.password.is_empty(),
AuthenticationError::InvalidPassword
);
let user = self
.db_client
.lock()
.unwrap()
.get_user(&request.id)
.map_err(|_| AuthenticationError::UserIdNotFound)?;
if !user.verify_password(&request.password) {
bail!(AuthenticationError::IncorrectPassword)
} else {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|e| AuthenticationServiceError::Service(e.into()))?;
let exp = (now + Duration::from_secs(24 * 60 * 60)).as_secs();
match user.get_token(exp, &self.jwt_secret) {
Ok(token) => Ok(Response::new(UserLoginResponse { token })),
Err(e) => bail!(AuthenticationServiceError::Service(e)),
}
}
}
async fn user_change_password(
&self,
request: Request<UserChangePasswordRequest>,
) -> TeaclaveServiceResponseResult<()> {
let requester_role = self.validate_credential_in_request(&request)?;
let id: String = request
.metadata()
.get("id")
.and_then(|x| x.to_str().ok())
.unwrap()
.into();
let request = request.get_ref();
ensure!(
!request.password.is_empty(),
AuthenticationError::InvalidPassword
);
let updated_user = UserInfo::new(&id, &request.password, requester_role);
match self.db_client.lock().unwrap().update_user(&updated_user) {
Ok(_) => Ok(Response::new(())),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
async fn reset_user_password(
&self,
request: Request<ResetUserPasswordRequest>,
) -> TeaclaveServiceResponseResult<ResetUserPasswordResponse> {
let requester_role = self.validate_credential_in_request(&request)?;
let request = request.get_ref();
ensure!(
!request.id.is_empty(),
AuthenticationServiceError::InvalidUserId
);
let user = self
.db_client
.lock()
.unwrap()
.get_user(&request.id)
.map_err(|_| AuthenticationServiceError::PermissionDenied)?;
ensure!(
authorize_reset_user_password(&requester_role, &user),
AuthenticationServiceError::PermissionDenied
);
let mut encode_buffer = uuid::Uuid::encode_buffer();
let new_password = uuid::Uuid::new_v4()
.to_simple()
.encode_lower(&mut encode_buffer);
let updated_user = UserInfo::new(&request.id, new_password, user.role);
match self.db_client.lock().unwrap().update_user(&updated_user) {
Ok(_) => Ok(Response::new(ResetUserPasswordResponse {
password: new_password.to_string(),
})),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
async fn delete_user(
&self,
request: Request<DeleteUserRequest>,
) -> TeaclaveServiceResponseResult<()> {
let requester_role = self.validate_credential_in_request(&request)?;
let request = request.get_ref();
ensure!(
!request.id.is_empty(),
AuthenticationServiceError::InvalidUserId
);
let user = self
.db_client
.lock()
.unwrap()
.get_user(&request.id)
.map_err(|_| AuthenticationServiceError::PermissionDenied)?;
ensure!(
authorize_delete_user(&requester_role, &user),
AuthenticationServiceError::PermissionDenied
);
match self.db_client.lock().unwrap().delete_user(&request.id) {
Ok(_) => Ok(Response::new(())),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
async fn list_users(
&self,
request: Request<ListUsersRequest>,
) -> TeaclaveServiceResponseResult<ListUsersResponse> {
let requester_role = self.validate_credential_in_request(&request)?;
let request = request.get_ref();
ensure!(
!request.id.is_empty(),
AuthenticationServiceError::InvalidUserId
);
ensure!(
authorize_list_users(&requester_role, request),
AuthenticationServiceError::PermissionDenied
);
let users = match requester_role {
UserRole::PlatformAdmin => self.db_client.lock().unwrap().list_users(),
_ => self
.db_client
.lock()
.unwrap()
.list_users_by_attribute(&request.id),
};
match users {
Ok(ids) => Ok(Response::new(ListUsersResponse { ids })),
Err(e) => bail!(AuthenticationServiceError::Service(e.into())),
}
}
}
fn authorize_user_register(role: &UserRole, request: &UserRegisterRequest) -> bool {
match role {
UserRole::PlatformAdmin => true,
UserRole::DataOwnerManager(s) => {
let request_role = UserRole::new(&request.role, &request.attribute);
request_role == UserRole::DataOwner(s.to_owned())
}
UserRole::FunctionOwner => {
let request_role = UserRole::new(&request.role, &request.attribute);
matches!(request_role, UserRole::DataOwnerManager(_))
}
_ => false,
}
}
fn authorize_user_update(role: &UserRole, request: &UserUpdateRequest) -> bool {
match role {
UserRole::PlatformAdmin => true,
UserRole::DataOwnerManager(s) => {
let request_role = UserRole::new(&request.role, &request.attribute);
request_role == UserRole::DataOwner(s.to_owned())
}
_ => false,
}
}
fn authorize_reset_user_password(role: &UserRole, target_user: &UserInfo) -> bool {
match role {
UserRole::PlatformAdmin => true,
UserRole::DataOwnerManager(s) => {
let request_role = &target_user.role;
*request_role == UserRole::DataOwner(s.to_owned())
}
_ => false,
}
}
fn authorize_delete_user(role: &UserRole, target_user: &UserInfo) -> bool {
match role {
UserRole::PlatformAdmin => true,
UserRole::DataOwnerManager(s) => {
let request_role = &target_user.role;
*request_role == UserRole::DataOwner(s.to_owned())
}
_ => false,
}
}
fn authorize_list_users(role: &UserRole, request: &ListUsersRequest) -> bool {
match role {
UserRole::PlatformAdmin => true,
UserRole::DataOwnerManager(s) => s == &request.id,
UserRole::FunctionOwner => false,
_ => false,
}
}
#[cfg(feature = "enclave_unit_test")]
pub mod tests {
use super::*;
use crate::user_db::*;
use crate::user_info::*;
use rand::RngCore;
use std::vec;
use teaclave_rpc::{IntoRequest, MetadataMap};
fn get_mock_service() -> TeaclaveAuthenticationApiService {
let database = Database::open("").unwrap();
let mut jwt_secret = vec![0; JWT_SECRET_LEN];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut jwt_secret);
let client = database.get_client();
crate::create_platform_admin_user(client, "admin", "teaclave").unwrap();
TeaclaveAuthenticationApiService {
db_client: Arc::new(Mutex::new(database.get_client())),
jwt_secret,
}
}
pub async fn test_user_register() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request =
UserRegisterRequest::new("test_register_id", "test_password", "PlatformAdmin", "")
.into_request();
let meta = request.metadata_mut();
*meta = metadata;
assert!(service.user_register(request).await.is_ok());
}
pub async fn test_user_update() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request =
UserRegisterRequest::new("test_update_id", "test_password", "PlatformAdmin", "")
.into_request();
let meta = request.metadata_mut();
*meta = metadata.clone();
assert!(service.user_register(request).await.is_ok());
let mut request =
UserUpdateRequest::new("test_update_id", "updated_password", "PlatformAdmin", "")
.into_request();
let meta = request.metadata_mut();
*meta = metadata.clone();
service.user_update(request).await.unwrap();
let mut request =
UserUpdateRequest::new("test_nonexist_id", "updated_password", "PlatformAdmin", "")
.into_request();
let meta = request.metadata_mut();
*meta = metadata;
assert!(service.user_update(request).await.is_err());
let request = UserLoginRequest::new("test_update_id", "updated_password").into_request();
let response = service.user_login(request).await;
assert!(response.is_ok());
}
pub async fn test_user_login() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request =
UserRegisterRequest::new("test_login_id", "test_password", "FunctionOwner", "")
.into_request();
let meta = request.metadata_mut();
*meta = metadata;
assert!(service.user_register(request).await.is_ok());
let request = UserLoginRequest::new("test_login_id", "test_password").into_request();
let response = service.user_login(request).await;
assert!(response.is_ok());
let token = response.unwrap().into_inner().token;
let user = service
.db_client
.lock()
.unwrap()
.get_user("test_login_id")
.unwrap();
assert!(user.validate_token(&service.jwt_secret, &token).is_ok());
debug!("saved user_info: {:?}", user);
let request = UserLoginRequest::new("test_login_id", "test_password1").into_request();
assert!(service.user_login(request).await.is_err());
}
pub async fn test_user_change_password() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request = UserRegisterRequest::new(
"test_user_change_password_id",
"test_password",
"PlatformAdmin",
"",
)
.into_request();
*request.metadata_mut() = metadata;
assert!(service.user_register(request).await.is_ok());
let request =
UserLoginRequest::new("test_user_change_password_id", "test_password").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "test_user_change_password_id".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request = UserChangePasswordRequest::new("updated_password").into_request();
*request.metadata_mut() = metadata.clone();
service.user_change_password(request).await.unwrap();
let mut request = UserChangePasswordRequest::new("").into_request();
*request.metadata_mut() = metadata;
assert!(service.user_change_password(request).await.is_err());
let request = UserLoginRequest::new("test_user_change_password_id", "updated_password")
.into_request();
let response = service.user_login(request).await;
assert!(response.is_ok());
}
pub async fn test_reset_user_password() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request = UserRegisterRequest::new(
"test_reset_user_password_id",
"test_password",
"PlatformAdmin",
"",
)
.into_request();
*request.metadata_mut() = metadata.clone();
assert!(service.user_register(request).await.is_ok());
let mut request =
ResetUserPasswordRequest::new("test_reset_user_password_id").into_request();
*request.metadata_mut() = metadata.clone();
let response = service.reset_user_password(request).await;
assert!(response.is_ok());
let request = UserLoginRequest::new(
"test_reset_user_password_id",
response.unwrap().into_inner().password,
)
.into_request();
let response = service.user_login(request).await;
assert!(response.is_ok());
}
pub async fn test_delete_user() {
let service = get_mock_service();
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request =
UserRegisterRequest::new("test_delete_user_id", "test_password", "FunctionOwner", "")
.into_request();
*request.metadata_mut() = metadata;
assert!(service.user_register(request).await.is_ok());
let request = UserLoginRequest::new("test_delete_user_id", "test_password").into_request();
let response = service.user_login(request).await;
assert!(response.is_ok());
let token = response.unwrap().into_inner().token;
let user = service
.db_client
.lock()
.unwrap()
.get_user("test_delete_user_id")
.unwrap();
assert!(user.validate_token(&service.jwt_secret, &token).is_ok());
let request = UserLoginRequest::new("admin", "teaclave").into_request();
let response = service.user_login(request).await.unwrap().into_inner();
let mut metadata = MetadataMap::new();
metadata.insert("id", "admin".parse().unwrap());
metadata.insert("token", response.token.parse().unwrap());
let mut request = DeleteUserRequest::new("test_delete_user_id").into_request();
*request.metadata_mut() = metadata;
assert!(service.delete_user(request).await.is_ok());
debug!("saved user_info: {:?}", user);
let request = UserLoginRequest::new("test_delete_user_id", "test_password").into_request();
assert!(service.user_login(request).await.is_err());
}
}