auth/
permission.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::Debug;
16use std::sync::Arc;
17
18use api::v1::greptime_request::Request;
19use common_telemetry::debug;
20use sql::statements::statement::Statement;
21
22use crate::error::{PermissionDeniedSnafu, Result};
23use crate::user_info::DefaultUserInfo;
24use crate::{PermissionCheckerRef, UserInfo, UserInfoRef};
25
26#[derive(Debug, Clone)]
27pub enum PermissionReq<'a> {
28    GrpcRequest(&'a Request),
29    SqlStatement(&'a Statement),
30    PromQuery,
31    LogQuery,
32    Opentsdb,
33    LineProtocol,
34    PromStoreWrite,
35    PromStoreRead,
36    Otlp,
37    LogWrite,
38    BulkInsert,
39}
40
41impl<'a> PermissionReq<'a> {
42    /// Returns true if the permission request is for read operations.
43    pub fn is_readonly(&self) -> bool {
44        match self {
45            PermissionReq::GrpcRequest(Request::Query(_))
46            | PermissionReq::PromQuery
47            | PermissionReq::LogQuery
48            | PermissionReq::PromStoreRead => true,
49            PermissionReq::SqlStatement(stmt) => stmt.is_readonly(),
50
51            PermissionReq::GrpcRequest(_)
52            | PermissionReq::Opentsdb
53            | PermissionReq::LineProtocol
54            | PermissionReq::PromStoreWrite
55            | PermissionReq::Otlp
56            | PermissionReq::LogWrite
57            | PermissionReq::BulkInsert => false,
58        }
59    }
60
61    /// Returns true if the permission request is for write operations.
62    pub fn is_write(&self) -> bool {
63        !self.is_readonly()
64    }
65}
66
67#[derive(Debug)]
68pub enum PermissionResp {
69    Allow,
70    Reject,
71}
72
73pub trait PermissionChecker: Send + Sync {
74    fn check_permission(
75        &self,
76        user_info: UserInfoRef,
77        req: PermissionReq,
78    ) -> Result<PermissionResp>;
79}
80
81impl PermissionChecker for Option<&PermissionCheckerRef> {
82    fn check_permission(
83        &self,
84        user_info: UserInfoRef,
85        req: PermissionReq,
86    ) -> Result<PermissionResp> {
87        match self {
88            Some(checker) => match checker.check_permission(user_info, req) {
89                Ok(PermissionResp::Reject) => PermissionDeniedSnafu.fail(),
90                Ok(PermissionResp::Allow) => Ok(PermissionResp::Allow),
91                Err(e) => Err(e),
92            },
93            None => Ok(PermissionResp::Allow),
94        }
95    }
96}
97
98/// The default permission checker implementation.
99/// It checks the permission mode of [DefaultUserInfo].
100pub struct DefaultPermissionChecker;
101
102impl DefaultPermissionChecker {
103    /// Returns a new [PermissionCheckerRef] instance.
104    pub fn arc() -> PermissionCheckerRef {
105        Arc::new(DefaultPermissionChecker)
106    }
107}
108
109impl PermissionChecker for DefaultPermissionChecker {
110    fn check_permission(
111        &self,
112        user_info: UserInfoRef,
113        req: PermissionReq,
114    ) -> Result<PermissionResp> {
115        if let Some(default_user) = user_info.as_any().downcast_ref::<DefaultUserInfo>() {
116            let permission_mode = default_user.permission_mode();
117
118            if req.is_readonly() && !permission_mode.can_read() {
119                debug!(
120                    "Permission denied: read operation not allowed, user = {}, permission = {}",
121                    default_user.username(),
122                    permission_mode.as_str()
123                );
124                return Ok(PermissionResp::Reject);
125            }
126
127            if req.is_write() && !permission_mode.can_write() {
128                debug!(
129                    "Permission denied: write operation not allowed, user = {}, permission = {}",
130                    default_user.username(),
131                    permission_mode.as_str()
132                );
133                return Ok(PermissionResp::Reject);
134            }
135        }
136
137        // default allow all
138        Ok(PermissionResp::Allow)
139    }
140}
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use crate::user_info::PermissionMode;
145
146    #[test]
147    fn test_default_permission_checker_allow_all_operations() {
148        let checker = DefaultPermissionChecker;
149        let user_info =
150            DefaultUserInfo::with_name_and_permission("test_user", PermissionMode::ReadWrite);
151
152        let read_req = PermissionReq::PromQuery;
153        let write_req = PermissionReq::PromStoreWrite;
154
155        let read_result = checker
156            .check_permission(user_info.clone(), read_req)
157            .unwrap();
158        let write_result = checker.check_permission(user_info, write_req).unwrap();
159
160        assert!(matches!(read_result, PermissionResp::Allow));
161        assert!(matches!(write_result, PermissionResp::Allow));
162    }
163
164    #[test]
165    fn test_default_permission_checker_readonly_user() {
166        let checker = DefaultPermissionChecker;
167        let user_info =
168            DefaultUserInfo::with_name_and_permission("readonly_user", PermissionMode::ReadOnly);
169
170        let read_req = PermissionReq::PromQuery;
171        let write_req = PermissionReq::PromStoreWrite;
172
173        let read_result = checker
174            .check_permission(user_info.clone(), read_req)
175            .unwrap();
176        let write_result = checker.check_permission(user_info, write_req).unwrap();
177
178        assert!(matches!(read_result, PermissionResp::Allow));
179        assert!(matches!(write_result, PermissionResp::Reject));
180    }
181
182    #[test]
183    fn test_default_permission_checker_writeonly_user() {
184        let checker = DefaultPermissionChecker;
185        let user_info =
186            DefaultUserInfo::with_name_and_permission("writeonly_user", PermissionMode::WriteOnly);
187
188        let read_req = PermissionReq::LogQuery;
189        let write_req = PermissionReq::LogWrite;
190
191        let read_result = checker
192            .check_permission(user_info.clone(), read_req)
193            .unwrap();
194        let write_result = checker.check_permission(user_info, write_req).unwrap();
195
196        assert!(matches!(read_result, PermissionResp::Reject));
197        assert!(matches!(write_result, PermissionResp::Allow));
198    }
199}