Skip to content

Commit

Permalink
[identity] [lib] [keyserver] [7/n] Add userID to reserved_usernames t…
Browse files Browse the repository at this point in the history
…able in DDB

Summary:
Adding userID to reserved_usernames table. This is to enable future work streaming usernames to opensearch instance.

Depends on D10438

Test Plan: Test on local dev. Registered new user on ios and successfully persisted userID attribute in dynamoDB.

Reviewers: varun, bartek

Reviewed By: varun

Subscribers: ashoat, tomek

Differential Revision: https://phab.comm.dev/D10480
  • Loading branch information
wyilio committed Jan 17, 2024
1 parent 4064a9a commit 1fbd675
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 35 deletions.
11 changes: 8 additions & 3 deletions keyserver/src/creators/account-creator.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
RegisterRequest,
} from 'lib/types/account-types.js';
import type {
UserDetail,
ReservedUsernameMessage,
SignedIdentityKeysBlob,
} from 'lib/types/crypto-types.js';
Expand Down Expand Up @@ -190,7 +191,9 @@ async function createAccount(
];

ignorePromiseRejections(
createAndSendReservedUsernameMessage([request.username]),
createAndSendReservedUsernameMessage([
{ username: request.username, userID: id },
]),
);

return {
Expand Down Expand Up @@ -248,7 +251,9 @@ async function processSIWEAccountCreation(
await processAccountCreationCommon(viewer);

ignorePromiseRejections(
createAndSendReservedUsernameMessage([request.address]),
createAndSendReservedUsernameMessage([
{ username: request.address, userID: id },
]),
);

return id;
Expand Down Expand Up @@ -349,7 +354,7 @@ async function processAccountCreationCommon(viewer: Viewer) {
}

async function createAndSendReservedUsernameMessage(
payload: $ReadOnlyArray<string>,
payload: $ReadOnlyArray<UserDetail>,
) {
const issuedAt = new Date().toISOString();
const reservedUsernameMessage: ReservedUsernameMessage = {
Expand Down
8 changes: 4 additions & 4 deletions keyserver/src/cron/update-identity-reserved-usernames.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import { getRustAPI } from 'rust-node-addon';

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<void> {
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);
Expand Down
12 changes: 12 additions & 0 deletions keyserver/src/fetchers/user-fetchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
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,
Expand Down Expand Up @@ -448,6 +449,16 @@ async function fetchAllUsernames(): Promise<string[]> {
return result.map(row => row.username);
}

async function fetchAllUserDetails(): Promise<UserDetail[]> {
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<?string> {
const changeRoleExtractString = `$.${threadPermissions.CHANGE_ROLE}`;
const query = SQL`
Expand Down Expand Up @@ -490,6 +501,7 @@ export {
fetchAllUserIDs,
fetchUsername,
fetchAllUsernames,
fetchAllUserDetails,
fetchKnownUserInfos,
fetchKeyserverAdminID,
fetchUserIDForEthereumAddress,
Expand Down
12 changes: 7 additions & 5 deletions lib/types/crypto-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,17 @@ export const signedIdentityKeysBlobValidator: TInterface<SignedIdentityKeysBlob>
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<string>,
+payload: $ReadOnlyArray<UserDetail>,
+issuedAt: string,
}
| {
Expand All @@ -82,10 +87,7 @@ export type ReservedUsernameMessage =
}
| {
+statement: 'This user is the owner of the following username and user ID',
+payload: {
+username: string,
+userID: string,
},
+payload: UserDetail,
+issuedAt: string,
};

Expand Down
8 changes: 4 additions & 4 deletions services/identity/src/client_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,20 +627,20 @@ impl IdentityClientService for ClientService {
) -> Result<tonic::Response<Empty>, 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)?;

Expand Down
1 change: 1 addition & 0 deletions services/identity/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub const NONCE_TABLE_EXPIRATION_TIME_UNIX_ATTRIBUTE: &str =
// 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
Expand Down
31 changes: 18 additions & 13 deletions services/identity/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::ddb_utils::{
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};
Expand All @@ -35,7 +36,8 @@ use crate::constants::{
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,
Expand Down Expand Up @@ -1036,19 +1038,18 @@ impl DatabaseClient {

pub async fn filter_out_taken_usernames(
&self,
usernames: Vec<String>,
) -> Result<Vec<String>, Error> {
user_details: Vec<UserDetail>,
) -> Result<Vec<UserDetail>, Error> {
let db_usernames = self.get_all_usernames().await?;

let db_usernames_set: HashSet<String> = db_usernames.into_iter().collect();
let usernames_set: HashSet<String> = usernames.into_iter().collect();

let available_usernames: Vec<String> = usernames_set
.difference(&db_usernames_set)
.cloned()
let available_user_details: Vec<UserDetail> = 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(
Expand Down Expand Up @@ -1395,17 +1396,21 @@ impl DatabaseClient {

pub async fn add_usernames_to_reserved_usernames_table(
&self,
usernames: Vec<String>,
user_details: Vec<UserDetail>,
) -> 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();

Expand Down
12 changes: 6 additions & 6 deletions services/identity/src/reserved_users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ struct Message<T> {
// `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<T: serde::de::DeserializeOwned>(
Expand Down Expand Up @@ -88,7 +88,7 @@ pub fn validate_account_ownership_message_and_get_user_id(
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::<UsernameAndID>(
let deserialized_message = validate_and_decode_message::<UserDetail>(
keyserver_message,
keyserver_signature,
EXPECTED_STATEMENT,
Expand All @@ -104,8 +104,8 @@ pub fn validate_account_ownership_message_and_get_user_id(
pub fn validate_add_reserved_usernames_message(
keyserver_message: &str,
keyserver_signature: &str,
) -> Result<Vec<String>, Status> {
let deserialized_message = validate_and_decode_message::<Vec<String>>(
) -> Result<Vec<UserDetail>, Status> {
let deserialized_message = validate_and_decode_message::<Vec<UserDetail>>(
keyserver_message,
keyserver_signature,
b"Add the following usernames to reserved list",
Expand Down

0 comments on commit 1fbd675

Please sign in to comment.