diff --git a/keyserver/src/creators/account-creator.js b/keyserver/src/creators/account-creator.js --- a/keyserver/src/creators/account-creator.js +++ b/keyserver/src/creators/account-creator.js @@ -13,6 +13,7 @@ RegisterRequest, } from 'lib/types/account-types.js'; import type { + UserDetail, ReservedUsernameMessage, SignedIdentityKeysBlob, } from 'lib/types/crypto-types.js'; @@ -190,7 +191,9 @@ ]; ignorePromiseRejections( - createAndSendReservedUsernameMessage([request.username]), + createAndSendReservedUsernameMessage([ + { username: request.username, userID: id }, + ]), ); return { @@ -248,7 +251,9 @@ await processAccountCreationCommon(viewer); ignorePromiseRejections( - createAndSendReservedUsernameMessage([request.address]), + createAndSendReservedUsernameMessage([ + { username: request.address, userID: id }, + ]), ); return id; @@ -349,7 +354,7 @@ } async function createAndSendReservedUsernameMessage( - payload: $ReadOnlyArray, + payload: $ReadOnlyArray, ) { const issuedAt = new Date().toISOString(); const reservedUsernameMessage: ReservedUsernameMessage = { diff --git a/keyserver/src/cron/update-identity-reserved-usernames.js b/keyserver/src/cron/update-identity-reserved-usernames.js --- a/keyserver/src/cron/update-identity-reserved-usernames.js +++ b/keyserver/src/cron/update-identity-reserved-usernames.js @@ -4,19 +4,19 @@ import type { ReservedUsernameMessage } from 'lib/types/crypto-types.js'; -import { fetchAllUsernames } from '../fetchers/user-fetchers.js'; +import { fetchAllUserDetails } from '../fetchers/user-fetchers.js'; import { fetchOlmAccount } from '../updaters/olm-account-updater.js'; async function updateIdentityReservedUsernames(): Promise { - const [usernames, rustAPI, accountInfo] = await Promise.all([ - fetchAllUsernames(), + const [userDetails, rustAPI, accountInfo] = await Promise.all([ + fetchAllUserDetails(), getRustAPI(), fetchOlmAccount('content'), ]); const issuedAt = new Date().toISOString(); const reservedUsernameMessage: ReservedUsernameMessage = { statement: 'Add the following usernames to reserved list', - payload: usernames, + payload: userDetails, issuedAt, }; const stringifiedMessage = JSON.stringify(reservedUsernameMessage); diff --git a/keyserver/src/fetchers/user-fetchers.js b/keyserver/src/fetchers/user-fetchers.js --- a/keyserver/src/fetchers/user-fetchers.js +++ b/keyserver/src/fetchers/user-fetchers.js @@ -7,6 +7,7 @@ FUTURE_CODE_VERSION, } from 'lib/shared/version-utils.js'; import type { AvatarDBContent, ClientAvatar } from 'lib/types/avatar-types.js'; +import type { UserDetail } from 'lib/types/crypto-types.js'; import { undirectedStatus, directedStatus, @@ -448,6 +449,16 @@ return result.map(row => row.username); } +async function fetchAllUserDetails(): Promise { + const query = SQL`SELECT username, id FROM users`; + const [result] = await dbQuery(query); + + return result.map(row => ({ + username: row.username, + userID: row.id, + })); +} + async function fetchKeyserverAdminID(): Promise { const changeRoleExtractString = `$.${threadPermissions.CHANGE_ROLE}`; const query = SQL` @@ -490,6 +501,7 @@ fetchAllUserIDs, fetchUsername, fetchAllUsernames, + fetchAllUserDetails, fetchKnownUserInfos, fetchKeyserverAdminID, fetchUserIDForEthereumAddress, diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js --- a/lib/types/crypto-types.js +++ b/lib/types/crypto-types.js @@ -67,12 +67,17 @@ signature: t.String, }); +export type UserDetail = { + +username: string, + +userID: string, +}; + // This type should not be changed without making equivalent changes to // `Message` in Identity service's `reserved_users` module export type ReservedUsernameMessage = | { +statement: 'Add the following usernames to reserved list', - +payload: $ReadOnlyArray, + +payload: $ReadOnlyArray, +issuedAt: string, } | { @@ -82,10 +87,7 @@ } | { +statement: 'This user is the owner of the following username and user ID', - +payload: { - +username: string, - +userID: string, - }, + +payload: UserDetail, +issuedAt: string, }; diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs --- a/services/identity/src/client_service.rs +++ b/services/identity/src/client_service.rs @@ -627,20 +627,20 @@ ) -> Result, tonic::Status> { let message = request.into_inner(); - let usernames = validate_add_reserved_usernames_message( + let user_details = validate_add_reserved_usernames_message( &message.message, &message.signature, )?; - let filtered_usernames = self + let filtered_user_details = self .client - .filter_out_taken_usernames(usernames) + .filter_out_taken_usernames(user_details) .await .map_err(handle_db_error)?; self .client - .add_usernames_to_reserved_usernames_table(filtered_usernames) + .add_usernames_to_reserved_usernames_table(filtered_user_details) .await .map_err(handle_db_error)?; diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs --- a/services/identity/src/constants.rs +++ b/services/identity/src/constants.rs @@ -89,6 +89,7 @@ // Usernames reserved because they exist in Ashoat's keyserver already pub const RESERVED_USERNAMES_TABLE: &str = "identity-reserved-usernames"; pub const RESERVED_USERNAMES_TABLE_PARTITION_KEY: &str = "username"; +pub const RESERVED_USERNAMES_TABLE_USER_ID_ATTRIBUTE: &str = "userID"; pub mod devices_table { /// table name diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs --- a/services/identity/src/database.rs +++ b/services/identity/src/database.rs @@ -21,6 +21,7 @@ OlmAccountType, }; use crate::error::{consume_error, Error}; +use crate::reserved_users::UserDetail; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use tracing::{debug, error, info, warn}; @@ -35,7 +36,8 @@ NONCE_TABLE_CREATED_ATTRIBUTE, NONCE_TABLE_EXPIRATION_TIME_ATTRIBUTE, NONCE_TABLE_EXPIRATION_TIME_UNIX_ATTRIBUTE, NONCE_TABLE_PARTITION_KEY, NOTIF_ONE_TIME_KEY, RESERVED_USERNAMES_TABLE, - RESERVED_USERNAMES_TABLE_PARTITION_KEY, USERS_TABLE, + RESERVED_USERNAMES_TABLE_PARTITION_KEY, + RESERVED_USERNAMES_TABLE_USER_ID_ATTRIBUTE, USERS_TABLE, USERS_TABLE_DEVICES_ATTRIBUTE, USERS_TABLE_DEVICES_MAP_CONTENT_ONE_TIME_KEYS_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_ATTRIBUTE_NAME, @@ -1036,19 +1038,18 @@ pub async fn filter_out_taken_usernames( &self, - usernames: Vec, - ) -> Result, Error> { + user_details: Vec, + ) -> Result, Error> { let db_usernames = self.get_all_usernames().await?; let db_usernames_set: HashSet = db_usernames.into_iter().collect(); - let usernames_set: HashSet = usernames.into_iter().collect(); - let available_usernames: Vec = usernames_set - .difference(&db_usernames_set) - .cloned() + let available_user_details: Vec = user_details + .into_iter() + .filter(|user_detail| !db_usernames_set.contains(&user_detail.username)) .collect(); - Ok(available_usernames) + Ok(available_user_details) } async fn get_user_from_user_info( @@ -1395,17 +1396,21 @@ pub async fn add_usernames_to_reserved_usernames_table( &self, - usernames: Vec, + user_details: Vec, ) -> Result<(), Error> { // A single call to BatchWriteItem can consist of up to 25 operations - for usernames_chunk in usernames.chunks(25) { - let write_requests = usernames_chunk + for user_chunk in user_details.chunks(25) { + let write_requests = user_chunk .iter() - .map(|username| { + .map(|user_detail| { let put_request = PutRequest::builder() .item( RESERVED_USERNAMES_TABLE_PARTITION_KEY, - AttributeValue::S(username.to_string()), + AttributeValue::S(user_detail.username.to_string()), + ) + .item( + RESERVED_USERNAMES_TABLE_USER_ID_ATTRIBUTE, + AttributeValue::S(user_detail.user_id.to_string()), ) .build(); diff --git a/services/identity/src/reserved_users.rs b/services/identity/src/reserved_users.rs --- a/services/identity/src/reserved_users.rs +++ b/services/identity/src/reserved_users.rs @@ -21,10 +21,10 @@ // `ReservedUsernameMessage` in lib/types/crypto-types.js #[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct UsernameAndID { - username: String, +pub struct UserDetail { + pub username: String, #[serde(rename = "userID")] - user_id: String, + pub user_id: String, } fn validate_and_decode_message( @@ -88,7 +88,7 @@ const EXPECTED_STATEMENT: &[u8; 60] = b"This user is the owner of the following username and user ID"; - let deserialized_message = validate_and_decode_message::( + let deserialized_message = validate_and_decode_message::( keyserver_message, keyserver_signature, EXPECTED_STATEMENT, @@ -104,8 +104,8 @@ pub fn validate_add_reserved_usernames_message( keyserver_message: &str, keyserver_signature: &str, -) -> Result, Status> { - let deserialized_message = validate_and_decode_message::>( +) -> Result, Status> { + let deserialized_message = validate_and_decode_message::>( keyserver_message, keyserver_signature, b"Add the following usernames to reserved list",