1use std::collections::{BTreeMap, BTreeSet};
11use std::sync::LazyLock;
12
13use mz_auth::AuthenticatorKind;
14use mz_repr::role_id::RoleId;
15use mz_repr::user::{ExternalUserMetadata, InternalUserMetadata};
16use serde::Serialize;
17
18pub const SYSTEM_USER_NAME: &str = "mz_system";
19pub static SYSTEM_USER: LazyLock<User> = LazyLock::new(|| User {
20 name: SYSTEM_USER_NAME.into(),
21 external_metadata: None,
22 internal_metadata: None,
23 authenticator_kind: None,
24});
25
26pub const SUPPORT_USER_NAME: &str = "mz_support";
27pub static SUPPORT_USER: LazyLock<User> = LazyLock::new(|| User {
28 name: SUPPORT_USER_NAME.into(),
29 external_metadata: None,
30 internal_metadata: None,
31 authenticator_kind: None,
32});
33
34pub const ANALYTICS_USER_NAME: &str = "mz_analytics";
35pub static ANALYTICS_USER: LazyLock<User> = LazyLock::new(|| User {
36 name: ANALYTICS_USER_NAME.into(),
37 external_metadata: None,
38 internal_metadata: None,
39 authenticator_kind: None,
40});
41
42pub static INTERNAL_USER_NAMES: LazyLock<BTreeSet<String>> = LazyLock::new(|| {
43 [&SYSTEM_USER, &SUPPORT_USER, &ANALYTICS_USER]
44 .into_iter()
45 .map(|user| user.name.clone())
46 .collect()
47});
48
49pub static INTERNAL_USER_NAME_TO_DEFAULT_CLUSTER: LazyLock<BTreeMap<String, String>> =
50 LazyLock::new(|| {
51 [
52 (&SYSTEM_USER, "mz_system"),
53 (&SUPPORT_USER, "mz_catalog_server"),
54 (&ANALYTICS_USER, "mz_analytics"),
55 ]
56 .into_iter()
57 .map(|(user, cluster)| (user.name.clone(), cluster.to_string()))
58 .collect()
59 });
60
61pub static HTTP_DEFAULT_USER: LazyLock<User> = LazyLock::new(|| User {
62 name: "anonymous_http_user".into(),
63 external_metadata: None,
64 internal_metadata: None,
65 authenticator_kind: None,
66});
67
68#[derive(Debug, Clone, Serialize)]
70pub struct User {
71 pub name: String,
73 pub external_metadata: Option<ExternalUserMetadata>,
75 pub internal_metadata: Option<InternalUserMetadata>,
78 pub authenticator_kind: Option<AuthenticatorKind>,
81}
82
83impl From<&User> for mz_pgwire_common::UserMetadata {
84 fn from(user: &User) -> mz_pgwire_common::UserMetadata {
85 mz_pgwire_common::UserMetadata {
86 is_admin: user.is_external_admin(),
87 should_limit_connections: user.limit_max_connections(),
88 }
89 }
90}
91
92impl PartialEq for User {
93 fn eq(&self, other: &User) -> bool {
94 self.name == other.name
95 }
96}
97
98impl User {
99 pub fn is_internal(&self) -> bool {
101 INTERNAL_USER_NAMES.contains(&self.name)
102 }
103
104 pub fn is_external_admin(&self) -> bool {
106 self.external_metadata
107 .as_ref()
108 .map(|metadata| metadata.admin)
109 .clone()
110 .unwrap_or(false)
111 }
112
113 pub fn is_internal_admin(&self) -> bool {
114 self.internal_metadata
115 .as_ref()
116 .map(|metadata| metadata.superuser)
117 .clone()
118 .unwrap_or(false)
119 }
120
121 pub fn is_superuser(&self) -> bool {
123 matches!(self.kind(), UserKind::Superuser)
124 }
125
126 pub fn is_system_user(&self) -> bool {
128 self == &*SYSTEM_USER
129 }
130
131 pub fn limit_max_connections(&self) -> bool {
133 !self.is_internal()
134 }
135
136 pub fn kind(&self) -> UserKind {
138 if self.is_external_admin() || self.is_system_user() || self.is_internal_admin() {
139 UserKind::Superuser
140 } else {
141 UserKind::Regular
142 }
143 }
144}
145
146#[derive(Debug, Copy, Clone)]
147pub enum UserKind {
148 Regular,
149 Superuser,
150}
151
152pub const MZ_SYSTEM_ROLE_ID: RoleId = RoleId::System(1);
153pub const MZ_SUPPORT_ROLE_ID: RoleId = RoleId::System(2);
154pub const MZ_ANALYTICS_ROLE_ID: RoleId = RoleId::System(3);
155pub const MZ_MONITOR_ROLE_ID: RoleId = RoleId::Predefined(1);
156pub const MZ_MONITOR_REDACTED_ROLE_ID: RoleId = RoleId::Predefined(2);
157
158#[derive(Debug, Clone)]
163pub struct RoleMetadata {
164 pub authenticated_role: RoleId,
166 pub session_role: RoleId,
170 pub current_role: RoleId,
173}
174
175impl RoleMetadata {
176 pub fn new(id: RoleId) -> RoleMetadata {
178 RoleMetadata {
179 authenticated_role: id,
180 session_role: id,
181 current_role: id,
182 }
183 }
184}