blob: 14f71f3af6f34b372df66d9e76ca874e1e575878 [file]
// 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::permissioner::Permissioner;
use crate::stm::StateHandler;
use crate::stm::snapshot::Snapshotable;
use crate::{collect_handlers, define_state, impl_fill_restore};
use ahash::AHashMap;
use iggy_common::change_password::ChangePassword;
use iggy_common::create_personal_access_token::CreatePersonalAccessToken;
use iggy_common::create_user::CreateUser;
use iggy_common::delete_personal_access_token::DeletePersonalAccessToken;
use iggy_common::delete_user::DeleteUser;
use iggy_common::update_permissions::UpdatePermissions;
use iggy_common::update_user::UpdateUser;
use iggy_common::{
GlobalPermissions, IggyTimestamp, Permissions, PersonalAccessToken, StreamPermissions, UserId,
UserStatus,
};
use serde::{Deserialize, Serialize};
use slab::Slab;
use std::sync::Arc;
// ============================================================================
// User Entity
// ============================================================================
#[derive(Debug, Clone)]
pub struct User {
pub id: UserId,
pub username: Arc<str>,
pub password_hash: Arc<str>,
pub status: UserStatus,
pub created_at: IggyTimestamp,
pub permissions: Option<Arc<Permissions>>,
}
impl Default for User {
fn default() -> Self {
Self {
id: 0,
username: Arc::from(""),
password_hash: Arc::from(""),
status: UserStatus::default(),
created_at: IggyTimestamp::default(),
permissions: None,
}
}
}
impl User {
pub fn new(
username: Arc<str>,
password_hash: Arc<str>,
status: UserStatus,
created_at: IggyTimestamp,
permissions: Option<Arc<Permissions>>,
) -> Self {
Self {
id: 0,
username,
password_hash,
status,
created_at,
permissions,
}
}
}
define_state! {
Users {
index: AHashMap<Arc<str>, UserId>,
items: Slab<User>,
personal_access_tokens: AHashMap<UserId, AHashMap<Arc<str>, PersonalAccessToken>>,
permissioner: Permissioner,
}
}
collect_handlers! {
Users {
CreateUser,
UpdateUser,
DeleteUser,
ChangePassword,
UpdatePermissions,
CreatePersonalAccessToken,
DeletePersonalAccessToken,
}
}
impl UsersInner {
fn resolve_user_id(&self, identifier: &iggy_common::Identifier) -> Option<usize> {
use iggy_common::IdKind;
match identifier.kind {
IdKind::Numeric => {
let id = identifier.get_u32_value().ok()? as usize;
if self.items.contains(id) {
Some(id)
} else {
None
}
}
IdKind::String => {
let username = identifier.get_string_value().ok()?;
self.index.get(username.as_str()).map(|&id| id as usize)
}
}
}
}
impl StateHandler for CreateUser {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
let username_arc: Arc<str> = Arc::from(self.username.as_str());
if state.index.contains_key(&username_arc) {
return;
}
let user = User {
id: 0,
username: username_arc.clone(),
password_hash: Arc::from(self.password.as_str()),
status: self.status,
created_at: iggy_common::IggyTimestamp::now(),
permissions: self.permissions.as_ref().map(|p| Arc::new(p.clone())),
};
let id = state.items.insert(user);
if let Some(user) = state.items.get_mut(id) {
user.id = id as UserId;
}
state.index.insert(username_arc, id as UserId);
state
.personal_access_tokens
.insert(id as UserId, AHashMap::default());
}
}
impl StateHandler for UpdateUser {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
let Some(user_id) = state.resolve_user_id(&self.user_id) else {
return;
};
let Some(user) = state.items.get_mut(user_id) else {
return;
};
if let Some(new_username) = &self.username {
let new_username_arc: Arc<str> = Arc::from(new_username.as_str());
if let Some(&existing_id) = state.index.get(&new_username_arc)
&& existing_id != user_id as UserId
{
return;
}
state.index.remove(&user.username);
user.username = new_username_arc.clone();
state.index.insert(new_username_arc, user_id as UserId);
}
if let Some(new_status) = self.status {
user.status = new_status;
}
}
}
impl StateHandler for DeleteUser {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
let Some(user_id) = state.resolve_user_id(&self.user_id) else {
return;
};
if let Some(user) = state.items.get(user_id) {
let username = user.username.clone();
state.items.remove(user_id);
state.index.remove(&username);
state.personal_access_tokens.remove(&(user_id as UserId));
}
}
}
impl StateHandler for ChangePassword {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
let Some(user_id) = state.resolve_user_id(&self.user_id) else {
return;
};
if let Some(user) = state.items.get_mut(user_id) {
user.password_hash = Arc::from(self.new_password.as_str());
}
}
}
impl StateHandler for UpdatePermissions {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
let Some(user_id) = state.resolve_user_id(&self.user_id) else {
return;
};
if let Some(user) = state.items.get_mut(user_id) {
user.permissions = self.permissions.as_ref().map(|p| Arc::new(p.clone()));
}
}
}
impl StateHandler for CreatePersonalAccessToken {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
// TODO: Stub until protocol gets adjusted.
let user_id = 0;
let user_tokens = state.personal_access_tokens.entry(user_id).or_default();
let name_arc: Arc<str> = Arc::from(self.name.as_str());
if user_tokens.contains_key(&name_arc) {
return;
}
let expiry_at = PersonalAccessToken::calculate_expiry_at(IggyTimestamp::now(), self.expiry);
if let Some(expiry_at) = expiry_at
&& expiry_at.as_micros() <= IggyTimestamp::now().as_micros()
{
return;
}
let (pat, _) = PersonalAccessToken::new(
user_id,
self.name.as_ref(),
IggyTimestamp::now(),
self.expiry,
);
user_tokens.insert(name_arc, pat);
}
}
impl StateHandler for DeletePersonalAccessToken {
type State = UsersInner;
fn apply(&self, state: &mut UsersInner) {
// TODO: Stub until protocol gets adjusted.
let user_id = 0;
if let Some(user_tokens) = state.personal_access_tokens.get_mut(&user_id) {
let name_arc: Arc<str> = Arc::from(self.name.as_str());
user_tokens.remove(&name_arc);
}
}
}
/// User snapshot representation for serialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSnapshot {
pub id: UserId,
pub username: String,
pub password_hash: String,
pub status: UserStatus,
pub created_at: IggyTimestamp,
pub permissions: Option<Permissions>,
}
/// Personal access token snapshot representation for serialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersonalAccessTokenSnapshot {
pub user_id: UserId,
pub name: String,
pub token: String,
pub expiry_at: Option<IggyTimestamp>,
}
/// Permissioner snapshot representation for serialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionerSnapshot {
pub users_permissions: Vec<(UserId, GlobalPermissions)>,
pub users_streams_permissions: Vec<((UserId, usize), StreamPermissions)>,
pub users_that_can_poll_messages_from_all_streams: Vec<UserId>,
pub users_that_can_send_messages_to_all_streams: Vec<UserId>,
pub users_that_can_poll_messages_from_specific_streams: Vec<(UserId, usize)>,
pub users_that_can_send_messages_to_specific_streams: Vec<(UserId, usize)>,
}
/// Snapshot representation for the Users state machine.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsersSnapshot {
pub items: Vec<(usize, UserSnapshot)>,
pub personal_access_tokens: Vec<(UserId, Vec<(String, PersonalAccessTokenSnapshot)>)>,
pub permissioner: PermissionerSnapshot,
}
impl Snapshotable for Users {
type Snapshot = UsersSnapshot;
fn to_snapshot(&self) -> Self::Snapshot {
self.inner.read(|inner| {
let items: Vec<(usize, UserSnapshot)> = inner
.items
.iter()
.map(|(user_id, user)| {
(
user_id,
UserSnapshot {
id: user.id,
username: user.username.to_string(),
password_hash: user.password_hash.to_string(),
status: user.status,
created_at: user.created_at,
permissions: user.permissions.as_ref().map(|p| (**p).clone()),
},
)
})
.collect();
let personal_access_tokens: Vec<(UserId, Vec<(String, PersonalAccessTokenSnapshot)>)> =
inner
.personal_access_tokens
.iter()
.map(|(&user_id, tokens)| {
let token_list: Vec<(String, PersonalAccessTokenSnapshot)> = tokens
.iter()
.map(|(name, pat)| {
(
name.to_string(),
PersonalAccessTokenSnapshot {
user_id: pat.user_id,
name: pat.name.to_string(),
token: pat.token.to_string(),
expiry_at: pat.expiry_at,
},
)
})
.collect();
(user_id, token_list)
})
.collect();
let permissioner = PermissionerSnapshot {
users_permissions: inner
.permissioner
.users_permissions
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect(),
users_streams_permissions: inner
.permissioner
.users_streams_permissions
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect(),
users_that_can_poll_messages_from_all_streams: inner
.permissioner
.users_that_can_poll_messages_from_all_streams
.iter()
.copied()
.collect(),
users_that_can_send_messages_to_all_streams: inner
.permissioner
.users_that_can_send_messages_to_all_streams
.iter()
.copied()
.collect(),
users_that_can_poll_messages_from_specific_streams: inner
.permissioner
.users_that_can_poll_messages_from_specific_streams
.iter()
.copied()
.collect(),
users_that_can_send_messages_to_specific_streams: inner
.permissioner
.users_that_can_send_messages_to_specific_streams
.iter()
.copied()
.collect(),
};
UsersSnapshot {
items,
personal_access_tokens,
permissioner,
}
})
}
fn from_snapshot(
snapshot: Self::Snapshot,
) -> Result<Self, crate::stm::snapshot::SnapshotError> {
let mut index: AHashMap<Arc<str>, UserId> = AHashMap::new();
let mut user_entries: Vec<(usize, User)> = Vec::new();
for (slab_key, user_snap) in snapshot.items {
let username: Arc<str> = Arc::from(user_snap.username.as_str());
let user = User {
id: user_snap.id,
username: username.clone(),
password_hash: Arc::from(user_snap.password_hash.as_str()),
status: user_snap.status,
created_at: user_snap.created_at,
permissions: user_snap.permissions.map(Arc::new),
};
index.insert(username, slab_key as UserId);
user_entries.push((slab_key, user));
}
let items: Slab<User> = user_entries.into_iter().collect();
let mut personal_access_tokens: AHashMap<UserId, AHashMap<Arc<str>, PersonalAccessToken>> =
AHashMap::new();
for (user_id, tokens) in snapshot.personal_access_tokens {
let mut token_map: AHashMap<Arc<str>, PersonalAccessToken> = AHashMap::new();
for (name, pat_snap) in tokens {
let pat = PersonalAccessToken::raw(
pat_snap.user_id,
&pat_snap.name,
&pat_snap.token,
pat_snap.expiry_at,
);
token_map.insert(Arc::from(name.as_str()), pat);
}
personal_access_tokens.insert(user_id, token_map);
}
let permissioner = Permissioner {
users_permissions: snapshot
.permissioner
.users_permissions
.into_iter()
.collect(),
users_streams_permissions: snapshot
.permissioner
.users_streams_permissions
.into_iter()
.collect(),
users_that_can_poll_messages_from_all_streams: snapshot
.permissioner
.users_that_can_poll_messages_from_all_streams
.into_iter()
.collect(),
users_that_can_send_messages_to_all_streams: snapshot
.permissioner
.users_that_can_send_messages_to_all_streams
.into_iter()
.collect(),
users_that_can_poll_messages_from_specific_streams: snapshot
.permissioner
.users_that_can_poll_messages_from_specific_streams
.into_iter()
.collect(),
users_that_can_send_messages_to_specific_streams: snapshot
.permissioner
.users_that_can_send_messages_to_specific_streams
.into_iter()
.collect(),
};
let inner = UsersInner {
index,
items,
personal_access_tokens,
permissioner,
};
Ok(inner.into())
}
}
impl_fill_restore!(Users, users);