Skip to main content

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 api::v1::query_request::Query;
20use common_telemetry::debug;
21use sql::statements::statement::Statement;
22
23use crate::error::{PermissionDeniedSnafu, Result};
24use crate::user_info::DefaultUserInfo;
25use crate::{PermissionCheckerRef, UserInfo, UserInfoRef};
26
27#[derive(Debug, Clone)]
28pub enum PermissionReq<'a> {
29    GrpcRequest(&'a Request),
30    SqlStatement(&'a Statement),
31    PromQuery,
32    LogQuery,
33    Opentsdb,
34    LineProtocol,
35    PromStoreWrite,
36    PromStoreRead,
37    Otlp,
38    LogWrite,
39    BulkInsert,
40}
41
42impl<'a> PermissionReq<'a> {
43    /// Returns true if the permission request is for read operations.
44    pub fn is_readonly(&self) -> bool {
45        match self {
46            PermissionReq::GrpcRequest(Request::Query(query_request)) => {
47                !matches!(query_request.query, Some(Query::InsertIntoPlan(_)))
48            }
49            PermissionReq::PromQuery | PermissionReq::LogQuery | PermissionReq::PromStoreRead => {
50                true
51            }
52            PermissionReq::SqlStatement(stmt) => stmt.is_readonly(),
53
54            PermissionReq::GrpcRequest(_)
55            | PermissionReq::Opentsdb
56            | PermissionReq::LineProtocol
57            | PermissionReq::PromStoreWrite
58            | PermissionReq::Otlp
59            | PermissionReq::LogWrite
60            | PermissionReq::BulkInsert => false,
61        }
62    }
63
64    /// Returns true if the permission request is for write operations.
65    pub fn is_write(&self) -> bool {
66        !self.is_readonly()
67    }
68}
69
70#[derive(Debug)]
71pub enum PermissionResp {
72    Allow,
73    Reject,
74}
75
76pub trait PermissionChecker: Send + Sync {
77    fn check_permission(
78        &self,
79        user_info: UserInfoRef,
80        req: PermissionReq,
81    ) -> Result<PermissionResp>;
82}
83
84impl PermissionChecker for Option<&PermissionCheckerRef> {
85    fn check_permission(
86        &self,
87        user_info: UserInfoRef,
88        req: PermissionReq,
89    ) -> Result<PermissionResp> {
90        match self {
91            Some(checker) => match checker.check_permission(user_info, req) {
92                Ok(PermissionResp::Reject) => PermissionDeniedSnafu.fail(),
93                Ok(PermissionResp::Allow) => Ok(PermissionResp::Allow),
94                Err(e) => Err(e),
95            },
96            None => Ok(PermissionResp::Allow),
97        }
98    }
99}
100
101/// The default permission checker implementation.
102/// It checks the permission mode of [DefaultUserInfo].
103pub struct DefaultPermissionChecker;
104
105impl DefaultPermissionChecker {
106    /// Returns a new [PermissionCheckerRef] instance.
107    pub fn arc() -> PermissionCheckerRef {
108        Arc::new(DefaultPermissionChecker)
109    }
110}
111
112impl PermissionChecker for DefaultPermissionChecker {
113    fn check_permission(
114        &self,
115        user_info: UserInfoRef,
116        req: PermissionReq,
117    ) -> Result<PermissionResp> {
118        if let Some(default_user) = user_info.as_any().downcast_ref::<DefaultUserInfo>() {
119            let permission_mode = default_user.permission_mode();
120
121            if req.is_readonly() && !permission_mode.can_read() {
122                debug!(
123                    "Permission denied: read operation not allowed, user = {}, permission = {}",
124                    default_user.username(),
125                    permission_mode.as_str()
126                );
127                return Ok(PermissionResp::Reject);
128            }
129
130            if req.is_write() && !permission_mode.can_write() {
131                debug!(
132                    "Permission denied: write operation not allowed, user = {}, permission = {}",
133                    default_user.username(),
134                    permission_mode.as_str()
135                );
136                return Ok(PermissionResp::Reject);
137            }
138        }
139
140        // default allow all
141        Ok(PermissionResp::Allow)
142    }
143}
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use crate::user_info::PermissionMode;
148
149    #[test]
150    fn test_default_permission_checker_allow_all_operations() {
151        let checker = DefaultPermissionChecker;
152        let user_info =
153            DefaultUserInfo::with_name_and_permission("test_user", PermissionMode::ReadWrite);
154
155        let read_req = PermissionReq::PromQuery;
156        let write_req = PermissionReq::PromStoreWrite;
157
158        let read_result = checker
159            .check_permission(user_info.clone(), read_req)
160            .unwrap();
161        let write_result = checker.check_permission(user_info, write_req).unwrap();
162
163        assert!(matches!(read_result, PermissionResp::Allow));
164        assert!(matches!(write_result, PermissionResp::Allow));
165    }
166
167    #[test]
168    fn test_default_permission_checker_readonly_user() {
169        let checker = DefaultPermissionChecker;
170        let user_info =
171            DefaultUserInfo::with_name_and_permission("readonly_user", PermissionMode::ReadOnly);
172
173        let read_req = PermissionReq::PromQuery;
174        let write_req = PermissionReq::PromStoreWrite;
175
176        let read_result = checker
177            .check_permission(user_info.clone(), read_req)
178            .unwrap();
179        let write_result = checker.check_permission(user_info, write_req).unwrap();
180
181        assert!(matches!(read_result, PermissionResp::Allow));
182        assert!(matches!(write_result, PermissionResp::Reject));
183    }
184
185    #[test]
186    fn test_default_permission_checker_writeonly_user() {
187        let checker = DefaultPermissionChecker;
188        let user_info =
189            DefaultUserInfo::with_name_and_permission("writeonly_user", PermissionMode::WriteOnly);
190
191        let read_req = PermissionReq::LogQuery;
192        let write_req = PermissionReq::LogWrite;
193
194        let read_result = checker
195            .check_permission(user_info.clone(), read_req)
196            .unwrap();
197        let write_result = checker.check_permission(user_info, write_req).unwrap();
198
199        assert!(matches!(read_result, PermissionResp::Reject));
200        assert!(matches!(write_result, PermissionResp::Allow));
201    }
202
203    #[test]
204    fn test_grpc_insert_into_plan_is_write_request() {
205        let request = Request::Query(api::v1::QueryRequest {
206            query: Some(Query::InsertIntoPlan(api::v1::InsertIntoPlan::default())),
207        });
208        let req = PermissionReq::GrpcRequest(&request);
209
210        assert!(req.is_write());
211    }
212}