MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / create_user

Function create_user

nodedb/src/control/server/pgwire/ddl/user/create.rs:39–137  ·  view source on GitHub ↗

CREATE USER [IF NOT EXISTS] WITH PASSWORD ' ' [ROLE ] [TENANT | TENANT ' ']

(
    state: &SharedState,
    identity: &AuthenticatedIdentity,
    username: &str,
    password: &str,
    role_name: Option<&str>,
    tenant: Option<&TenantSelector>,
    if_not_exists: bool,
)

Source from the content-addressed store, hash-verified

37/// CREATE USER [IF NOT EXISTS] <name> WITH PASSWORD '<password>' [ROLE <role>]
38/// [TENANT <id> | TENANT '<name>']
39pub fn create_user(
40 state: &SharedState,
41 identity: &AuthenticatedIdentity,
42 username: &str,
43 password: &str,
44 role_name: Option<&str>,
45 tenant: Option<&TenantSelector>,
46 if_not_exists: bool,
47) -> PgWireResult<Vec<Response>> {
48 require_tenant_admin(identity, "create users")?;
49
50 if username.is_empty() {
51 return Err(sqlstate_error(
52 "42601",
53 "syntax: CREATE USER <name> WITH PASSWORD '<password>' [ROLE <role>] [TENANT <id>]",
54 ));
55 }
56
57 // `IF NOT EXISTS`: re-creating an existing user is a no-op success.
58 if if_not_exists && state.credentials.get_user(username).is_some() {
59 return Ok(vec![Response::Execution(Tag::new("CREATE USER"))]);
60 }
61
62 if password.is_empty() {
63 return Err(sqlstate_error(
64 "42601",
65 "password must be a single-quoted string",
66 ));
67 }
68
69 let role = role_name.map(parse_role).unwrap_or(Role::ReadWrite);
70 let tenant_id = if let Some(selector) = tenant {
71 if !identity.is_superuser {
72 return Err(sqlstate_error("42501", "only superuser can assign tenants"));
73 }
74 resolve_tenant_selector(state, selector)?
75 } else {
76 identity.tenant_id
77 };
78
79 // Build the full `StoredUser` locally (hash + salt + user_id).
80 // Followers cannot reproduce the random salt, so this step
81 // MUST happen on the proposer node. The computed record is
82 // then replicated verbatim.
83 let stored = state
84 .credentials
85 .prepare_user(username, password, tenant_id, vec![role])
86 .map_err(|e| sqlstate_error("42710", &e.to_string()))?;
87
88 let entry = crate::control::catalog_entry::CatalogEntry::PutUser(Box::new(stored.clone()));
89 let log_index = crate::control::metadata_proposer::propose_catalog_entry(state, &entry)
90 .map_err(|e| sqlstate_error("XX000", &format!("metadata propose: {e}")))?;
91 if log_index == 0 {
92 // Single-node / no-cluster fallback: install into the
93 // in-memory cache so subsequent reads see the user.
94 // Persist to redb when a catalog is wired up — the
95 // catalog write is best-effort durability, not a gate
96 // on the cache update. Test fixtures (and any future

Callers 1

try_dispatch_syncFunction · 0.50

Calls 13

require_tenant_adminFunction · 0.85
resolve_tenant_selectorFunction · 0.85
propose_catalog_entryFunction · 0.85
prepare_userMethod · 0.80
to_stringMethod · 0.80
put_userMethod · 0.80
audit_recordMethod · 0.80
sqlstate_errorFunction · 0.50
is_emptyMethod · 0.45
get_userMethod · 0.45
cloneMethod · 0.45

Tested by

no test coverage detected