Skip to main content

mz_sql/session/
user.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::collections::{BTreeMap, BTreeSet};
11use std::sync::LazyLock;
12
13use mz_repr::role_id::RoleId;
14use mz_repr::user::{ExternalUserMetadata, InternalUserMetadata};
15use serde::Serialize;
16
17pub const SYSTEM_USER_NAME: &str = "mz_system";
18pub static SYSTEM_USER: LazyLock<User> = LazyLock::new(|| User {
19    name: SYSTEM_USER_NAME.into(),
20    external_metadata: None,
21    internal_metadata: None,
22});
23
24pub const SUPPORT_USER_NAME: &str = "mz_support";
25pub static SUPPORT_USER: LazyLock<User> = LazyLock::new(|| User {
26    name: SUPPORT_USER_NAME.into(),
27    external_metadata: None,
28    internal_metadata: None,
29});
30
31pub const ANALYTICS_USER_NAME: &str = "mz_analytics";
32pub static ANALYTICS_USER: LazyLock<User> = LazyLock::new(|| User {
33    name: ANALYTICS_USER_NAME.into(),
34    external_metadata: None,
35    internal_metadata: None,
36});
37
38pub static INTERNAL_USER_NAMES: LazyLock<BTreeSet<String>> = LazyLock::new(|| {
39    [&SYSTEM_USER, &SUPPORT_USER, &ANALYTICS_USER]
40        .into_iter()
41        .map(|user| user.name.clone())
42        .collect()
43});
44
45pub static INTERNAL_USER_NAME_TO_DEFAULT_CLUSTER: LazyLock<BTreeMap<String, String>> =
46    LazyLock::new(|| {
47        [
48            (&SYSTEM_USER, "mz_system"),
49            (&SUPPORT_USER, "mz_catalog_server"),
50            (&ANALYTICS_USER, "mz_analytics"),
51        ]
52        .into_iter()
53        .map(|(user, cluster)| (user.name.clone(), cluster.to_string()))
54        .collect()
55    });
56
57pub static HTTP_DEFAULT_USER: LazyLock<User> = LazyLock::new(|| User {
58    name: "anonymous_http_user".into(),
59    external_metadata: None,
60    internal_metadata: None,
61});
62
63/// Identifies a user.
64#[derive(Debug, Clone, Serialize)]
65pub struct User {
66    /// The name of the user within the system.
67    pub name: String,
68    /// Metadata about this user in an external system.
69    pub external_metadata: Option<ExternalUserMetadata>,
70    /// Metadata about this user stored in the catalog,
71    /// such as its role's `SUPERUSER` attribute.
72    pub internal_metadata: Option<InternalUserMetadata>,
73}
74
75impl From<&User> for mz_pgwire_common::UserMetadata {
76    fn from(user: &User) -> mz_pgwire_common::UserMetadata {
77        mz_pgwire_common::UserMetadata {
78            is_admin: user.is_external_admin(),
79            should_limit_connections: user.limit_max_connections(),
80        }
81    }
82}
83
84impl PartialEq for User {
85    fn eq(&self, other: &User) -> bool {
86        self.name == other.name
87    }
88}
89
90impl User {
91    /// Returns whether this is an internal user.
92    pub fn is_internal(&self) -> bool {
93        INTERNAL_USER_NAMES.contains(&self.name)
94    }
95
96    /// Returns whether this user is an admin in an external system.
97    pub fn is_external_admin(&self) -> bool {
98        self.external_metadata
99            .as_ref()
100            .map(|metadata| metadata.admin)
101            .clone()
102            .unwrap_or(false)
103    }
104
105    pub fn is_internal_admin(&self) -> bool {
106        self.internal_metadata
107            .as_ref()
108            .map(|metadata| metadata.superuser)
109            .clone()
110            .unwrap_or(false)
111    }
112
113    /// Returns whether this user is a superuser.
114    pub fn is_superuser(&self) -> bool {
115        matches!(self.kind(), UserKind::Superuser)
116    }
117
118    /// Returns whether this is user is the `mz_system` user.
119    pub fn is_system_user(&self) -> bool {
120        self == &*SYSTEM_USER
121    }
122
123    /// Returns whether we should limit this user's connections to max_connections
124    pub fn limit_max_connections(&self) -> bool {
125        !self.is_internal()
126    }
127
128    /// Returns the kind of user this is.
129    pub fn kind(&self) -> UserKind {
130        if self.is_external_admin() || self.is_system_user() || self.is_internal_admin() {
131            UserKind::Superuser
132        } else {
133            UserKind::Regular
134        }
135    }
136}
137
138#[derive(Debug, Copy, Clone)]
139pub enum UserKind {
140    Regular,
141    Superuser,
142}
143
144pub const MZ_SYSTEM_ROLE_ID: RoleId = RoleId::System(1);
145pub const MZ_SUPPORT_ROLE_ID: RoleId = RoleId::System(2);
146pub const MZ_ANALYTICS_ROLE_ID: RoleId = RoleId::System(3);
147pub const MZ_MONITOR_ROLE_ID: RoleId = RoleId::Predefined(1);
148pub const MZ_MONITOR_REDACTED_ROLE_ID: RoleId = RoleId::Predefined(2);
149
150/// Metadata about a Session's role.
151///
152/// Modeled after PostgreSQL role hierarchy:
153/// <https://github.com/postgres/postgres/blob/9089287aa037fdecb5a52cec1926e5ae9569e9f9/src/backend/utils/init/miscinit.c#L461-L493>
154#[derive(Debug, Clone)]
155pub struct RoleMetadata {
156    /// The role that initiated the database context. Fixed for the duration of the connection.
157    pub authenticated_role: RoleId,
158    /// Initially the same as `authenticated_role`, but can be changed by SET SESSION AUTHORIZATION
159    /// (not yet implemented). Used to determine what roles can be used for SET ROLE
160    /// (not yet implemented).
161    pub session_role: RoleId,
162    /// The role of the current execution context. This role is used for all normal privilege
163    /// checks.
164    pub current_role: RoleId,
165}
166
167impl RoleMetadata {
168    /// Returns a RoleMetadata with all fields set to `id`.
169    pub fn new(id: RoleId) -> RoleMetadata {
170        RoleMetadata {
171            authenticated_role: id,
172            session_role: id,
173            current_role: id,
174        }
175    }
176}