1use std::fmt::Debug;
16use std::sync::Arc;
17
18use api::v1::RowInsertRequests;
19use api::v1::greptime_request::Request;
20use api::v1::query_request::Query;
21use common_telemetry::debug;
22use sql::statements::statement::Statement;
23
24use crate::error::{PermissionDeniedSnafu, Result};
25use crate::user_info::DefaultUserInfo;
26use crate::{PermissionCheckerRef, UserInfo, UserInfoRef};
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33pub struct PermissionTableTarget {
34 pub catalog: String,
35 pub schema: String,
36 pub table: String,
37}
38
39impl PermissionTableTarget {
40 pub fn new(
42 catalog: impl Into<String>,
43 schema: impl Into<String>,
44 table: impl Into<String>,
45 ) -> Self {
46 Self {
47 catalog: catalog.into(),
48 schema: schema.into(),
49 table: table.into(),
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
61pub enum PermissionTableTargets {
62 Resolved(Vec<PermissionTableTarget>),
64 Unresolved,
66}
67
68impl PermissionTableTargets {
69 pub fn resolved(targets: Vec<PermissionTableTarget>) -> Self {
71 if targets.iter().any(|target| {
72 target.catalog.is_empty() || target.schema.is_empty() || target.table.is_empty()
73 }) {
74 Self::Unresolved
75 } else {
76 Self::Resolved(targets)
77 }
78 }
79
80 pub fn from_row_insert_requests(
82 catalog: &str,
83 schema: &str,
84 requests: &RowInsertRequests,
85 ) -> Self {
86 Self::resolved(
87 requests
88 .inserts
89 .iter()
90 .map(|request| PermissionTableTarget::new(catalog, schema, &request.table_name))
91 .collect(),
92 )
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum AccessMode {
99 Read,
100 Write,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct PermissionAction {
106 name: &'static str,
107 access_mode: AccessMode,
108}
109
110impl PermissionAction {
111 pub const fn read(name: &'static str) -> Self {
113 Self {
114 name,
115 access_mode: AccessMode::Read,
116 }
117 }
118
119 pub const fn write(name: &'static str) -> Self {
121 Self {
122 name,
123 access_mode: AccessMode::Write,
124 }
125 }
126
127 pub const fn name(self) -> &'static str {
129 self.name
130 }
131
132 pub const fn access_mode(self) -> AccessMode {
134 self.access_mode
135 }
136}
137
138pub const PROMQL_QUERY: PermissionAction = PermissionAction::read("promql.query");
139pub const LOG_QUERY: PermissionAction = PermissionAction::read("log.query");
140pub const OPENTSDB_WRITE: PermissionAction = PermissionAction::write("opentsdb.write");
141pub const INFLUXDB_WRITE: PermissionAction = PermissionAction::write("influxdb.write");
142pub const PROM_STORE_WRITE: PermissionAction = PermissionAction::write("prom_store.write");
143pub const PROM_STORE_READ: PermissionAction = PermissionAction::read("prom_store.read");
144pub const OTLP_WRITE: PermissionAction = PermissionAction::write("otlp.write");
145pub const LOG_WRITE: PermissionAction = PermissionAction::write("log.write");
146pub const JAEGER_QUERY: PermissionAction = PermissionAction::read("jaeger.query");
147pub const PIPELINE_QUERY: PermissionAction = PermissionAction::read("pipeline.query");
148pub const PIPELINE_INSERT: PermissionAction = PermissionAction::write("pipeline.insert");
149pub const PIPELINE_DELETE: PermissionAction = PermissionAction::write("pipeline.delete");
150pub const DASHBOARD_QUERY: PermissionAction = PermissionAction::read("dashboard.query");
151pub const DASHBOARD_SAVE: PermissionAction = PermissionAction::write("dashboard.save");
152pub const DASHBOARD_DELETE: PermissionAction = PermissionAction::write("dashboard.delete");
153pub const SEMANTIC_GRAPH_QUERY: PermissionAction = PermissionAction::read("semantic_graph.query");
157
158pub const ALL_ACTIONS: &[PermissionAction] = &[
163 PROMQL_QUERY,
164 LOG_QUERY,
165 OPENTSDB_WRITE,
166 INFLUXDB_WRITE,
167 PROM_STORE_WRITE,
168 PROM_STORE_READ,
169 OTLP_WRITE,
170 LOG_WRITE,
171 JAEGER_QUERY,
172 PIPELINE_QUERY,
173 PIPELINE_INSERT,
174 PIPELINE_DELETE,
175 DASHBOARD_QUERY,
176 DASHBOARD_SAVE,
177 DASHBOARD_DELETE,
178 SEMANTIC_GRAPH_QUERY,
179];
180
181#[derive(Debug, Clone)]
182pub enum PermissionReq<'a> {
183 GrpcRequest(&'a Request),
184 SqlStatement(&'a Statement),
185 Action(PermissionAction),
186 BulkInsert {
187 catalog: &'a str,
188 schema: &'a str,
189 table: &'a str,
190 },
191}
192
193impl<'a> PermissionReq<'a> {
194 pub fn is_readonly(&self) -> bool {
196 match self {
197 PermissionReq::GrpcRequest(Request::Query(query_request)) => {
198 !matches!(query_request.query, Some(Query::InsertIntoPlan(_)))
199 }
200 PermissionReq::SqlStatement(stmt) => stmt.is_readonly(),
201 PermissionReq::Action(action) => action.access_mode() == AccessMode::Read,
202
203 PermissionReq::GrpcRequest(_) | PermissionReq::BulkInsert { .. } => false,
204 }
205 }
206
207 pub fn is_write(&self) -> bool {
209 !self.is_readonly()
210 }
211}
212
213#[derive(Debug)]
214pub enum PermissionResp {
215 Allow,
216 Reject,
217}
218
219pub trait PermissionChecker: Send + Sync {
220 fn check_permission(
221 &self,
222 user_info: UserInfoRef,
223 req: PermissionReq,
224 ) -> Result<PermissionResp>;
225
226 fn check_permission_with_context(
227 &self,
228 user_info: UserInfoRef,
229 req: PermissionReq,
230 _current_schema: Option<&str>,
231 ) -> Result<PermissionResp> {
232 self.check_permission(user_info, req)
233 }
234
235 fn uses_table_targets(&self) -> bool {
241 true
242 }
243
244 fn check_permission_with_table_targets(
252 &self,
253 user_info: UserInfoRef,
254 req: PermissionReq,
255 targets: PermissionTableTargets,
256 ) -> Result<PermissionResp>;
257}
258
259fn check_permission_result(result: Result<PermissionResp>) -> Result<PermissionResp> {
260 match result {
261 Ok(PermissionResp::Reject) => PermissionDeniedSnafu.fail(),
262 Ok(PermissionResp::Allow) => Ok(PermissionResp::Allow),
263 Err(e) => Err(e),
264 }
265}
266
267impl PermissionChecker for Option<&PermissionCheckerRef> {
268 fn check_permission(
269 &self,
270 user_info: UserInfoRef,
271 req: PermissionReq,
272 ) -> Result<PermissionResp> {
273 self.check_permission_with_context(user_info, req, None)
274 }
275
276 fn check_permission_with_context(
277 &self,
278 user_info: UserInfoRef,
279 req: PermissionReq,
280 current_schema: Option<&str>,
281 ) -> Result<PermissionResp> {
282 match self {
283 Some(checker) => check_permission_result(checker.check_permission_with_context(
284 user_info,
285 req,
286 current_schema,
287 )),
288 None => Ok(PermissionResp::Allow),
289 }
290 }
291
292 fn uses_table_targets(&self) -> bool {
293 match self {
294 Some(checker) => checker.uses_table_targets(),
295 None => false,
296 }
297 }
298
299 fn check_permission_with_table_targets(
300 &self,
301 user_info: UserInfoRef,
302 req: PermissionReq,
303 targets: PermissionTableTargets,
304 ) -> Result<PermissionResp> {
305 match self {
306 Some(checker) => check_permission_result(
307 checker.check_permission_with_table_targets(user_info, req, targets),
308 ),
309 None => Ok(PermissionResp::Allow),
310 }
311 }
312}
313
314pub struct DefaultPermissionChecker;
317
318impl DefaultPermissionChecker {
319 pub fn arc() -> PermissionCheckerRef {
321 Arc::new(DefaultPermissionChecker)
322 }
323}
324
325impl PermissionChecker for DefaultPermissionChecker {
326 fn check_permission(
327 &self,
328 user_info: UserInfoRef,
329 req: PermissionReq,
330 ) -> Result<PermissionResp> {
331 if let Some(default_user) = user_info.as_any().downcast_ref::<DefaultUserInfo>() {
332 let permission_mode = default_user.permission_mode();
333
334 if req.is_readonly() && !permission_mode.can_read() {
335 debug!(
336 "Permission denied: read operation not allowed, user = {}, permission = {}",
337 default_user.username(),
338 permission_mode.as_str()
339 );
340 return Ok(PermissionResp::Reject);
341 }
342
343 if req.is_write() && !permission_mode.can_write() {
344 debug!(
345 "Permission denied: write operation not allowed, user = {}, permission = {}",
346 default_user.username(),
347 permission_mode.as_str()
348 );
349 return Ok(PermissionResp::Reject);
350 }
351 }
352
353 Ok(PermissionResp::Allow)
355 }
356
357 fn uses_table_targets(&self) -> bool {
358 false
359 }
360
361 fn check_permission_with_table_targets(
362 &self,
363 user_info: UserInfoRef,
364 req: PermissionReq,
365 _targets: PermissionTableTargets,
366 ) -> Result<PermissionResp> {
367 self.check_permission(user_info, req)
368 }
369}
370#[cfg(test)]
371mod tests {
372 use std::collections::HashSet;
373
374 use super::*;
375 use crate::error::{Error, InternalStateSnafu};
376 use crate::user_info::PermissionMode;
377
378 struct TargetAwarePermissionChecker;
379
380 impl PermissionChecker for TargetAwarePermissionChecker {
381 fn check_permission(
382 &self,
383 _user_info: UserInfoRef,
384 _req: PermissionReq,
385 ) -> Result<PermissionResp> {
386 Ok(PermissionResp::Reject)
387 }
388
389 fn check_permission_with_table_targets(
390 &self,
391 _user_info: UserInfoRef,
392 req: PermissionReq,
393 targets: PermissionTableTargets,
394 ) -> Result<PermissionResp> {
395 if !matches!(req, PermissionReq::Action(PROM_STORE_READ)) {
396 return Ok(PermissionResp::Reject);
397 }
398 let PermissionTableTargets::Resolved(targets) = targets else {
399 return Ok(PermissionResp::Reject);
400 };
401 if targets.iter().any(|target| target.table == "error") {
402 return InternalStateSnafu {
403 msg: "testing".to_string(),
404 }
405 .fail();
406 }
407 Ok(if targets.iter().all(|target| target.table == "allowed") {
408 PermissionResp::Allow
409 } else {
410 PermissionResp::Reject
411 })
412 }
413 }
414
415 fn resolved_targets(table: &str) -> PermissionTableTargets {
416 PermissionTableTargets::resolved(vec![PermissionTableTarget::new(
417 "greptime", "public", table,
418 )])
419 }
420
421 #[test]
422 fn test_resolve_permission_table_targets() {
423 assert_eq!(
424 PermissionTableTargets::Resolved(Vec::new()),
425 PermissionTableTargets::resolved(Vec::new())
426 );
427 assert_eq!(
428 resolved_targets("metrics"),
429 PermissionTableTargets::Resolved(vec![PermissionTableTarget::new(
430 "greptime", "public", "metrics"
431 )])
432 );
433
434 for target in [
435 PermissionTableTarget::new("", "public", "metrics"),
436 PermissionTableTarget::new("greptime", "", "metrics"),
437 PermissionTableTarget::new("greptime", "public", ""),
438 ] {
439 assert_eq!(
440 PermissionTableTargets::Unresolved,
441 PermissionTableTargets::resolved(vec![target])
442 );
443 }
444 }
445
446 #[test]
447 fn test_resolve_row_insert_request_targets() {
448 let requests = RowInsertRequests {
449 inserts: ["cpu", "mem"]
450 .into_iter()
451 .map(|table_name| api::v1::RowInsertRequest {
452 table_name: table_name.to_string(),
453 ..Default::default()
454 })
455 .collect(),
456 };
457
458 assert_eq!(
459 PermissionTableTargets::Resolved(vec![
460 PermissionTableTarget::new("greptime", "public", "cpu"),
461 PermissionTableTarget::new("greptime", "public", "mem"),
462 ]),
463 PermissionTableTargets::from_row_insert_requests("greptime", "public", &requests)
464 );
465 assert_eq!(
466 PermissionTableTargets::Unresolved,
467 PermissionTableTargets::from_row_insert_requests("", "public", &requests)
468 );
469 assert_eq!(
470 PermissionTableTargets::Unresolved,
471 PermissionTableTargets::from_row_insert_requests("greptime", "", &requests)
472 );
473
474 let unresolved = RowInsertRequests {
475 inserts: vec![api::v1::RowInsertRequest::default()],
476 };
477 assert_eq!(
478 PermissionTableTargets::Unresolved,
479 PermissionTableTargets::from_row_insert_requests("greptime", "public", &unresolved)
480 );
481
482 let empty = RowInsertRequests::default();
483 assert_eq!(
484 PermissionTableTargets::Resolved(Vec::new()),
485 PermissionTableTargets::from_row_insert_requests("greptime", "public", &empty)
486 );
487 }
488
489 #[test]
490 fn test_default_permission_checker_allow_all_operations() {
491 let checker = DefaultPermissionChecker;
492 let user_info =
493 DefaultUserInfo::with_name_and_permission("test_user", PermissionMode::ReadWrite);
494
495 let read_req = PermissionReq::Action(PROMQL_QUERY);
496 let write_req = PermissionReq::Action(PROM_STORE_WRITE);
497
498 let read_result = checker
499 .check_permission(user_info.clone(), read_req)
500 .unwrap();
501 let write_result = checker.check_permission(user_info, write_req).unwrap();
502
503 assert!(matches!(read_result, PermissionResp::Allow));
504 assert!(matches!(write_result, PermissionResp::Allow));
505 }
506
507 #[test]
508 fn test_default_permission_checker_readonly_user() {
509 let checker = DefaultPermissionChecker;
510 let user_info =
511 DefaultUserInfo::with_name_and_permission("readonly_user", PermissionMode::ReadOnly);
512
513 let read_req = PermissionReq::Action(PROMQL_QUERY);
514 let write_req = PermissionReq::Action(PROM_STORE_WRITE);
515
516 let read_result = checker
517 .check_permission(user_info.clone(), read_req)
518 .unwrap();
519 let write_result = checker.check_permission(user_info, write_req).unwrap();
520
521 assert!(matches!(read_result, PermissionResp::Allow));
522 assert!(matches!(write_result, PermissionResp::Reject));
523 }
524
525 #[test]
526 fn test_default_permission_checker_writeonly_user() {
527 let checker = DefaultPermissionChecker;
528 let user_info =
529 DefaultUserInfo::with_name_and_permission("writeonly_user", PermissionMode::WriteOnly);
530
531 let read_req = PermissionReq::Action(LOG_QUERY);
532 let write_req = PermissionReq::Action(LOG_WRITE);
533
534 let read_result = checker
535 .check_permission(user_info.clone(), read_req)
536 .unwrap();
537 let write_result = checker.check_permission(user_info, write_req).unwrap();
538
539 assert!(matches!(read_result, PermissionResp::Reject));
540 assert!(matches!(write_result, PermissionResp::Allow));
541 }
542
543 #[test]
544 fn test_grpc_insert_into_plan_is_write_request() {
545 let request = Request::Query(api::v1::QueryRequest {
546 query: Some(Query::InsertIntoPlan(api::v1::InsertIntoPlan::default())),
547 });
548 let req = PermissionReq::GrpcRequest(&request);
549
550 assert!(req.is_write());
551 }
552
553 #[test]
554 fn test_bulk_insert_is_write_request() {
555 let req = PermissionReq::BulkInsert {
556 catalog: "greptime",
557 schema: "public",
558 table: "metrics",
559 };
560
561 assert!(req.is_write());
562 }
563
564 #[test]
565 fn test_action_access_modes() {
566 let checker = DefaultPermissionChecker;
567 let mut names = HashSet::new();
568
569 for &action in ALL_ACTIONS {
570 assert!(
571 names.insert(action.name()),
572 "duplicate permission action: {}",
573 action.name()
574 );
575 let access_mode = action.access_mode();
576 let req = PermissionReq::Action(action);
577 assert_eq!(
578 access_mode == AccessMode::Read,
579 req.is_readonly(),
580 "{}",
581 action.name()
582 );
583
584 for (permission_mode, allowed) in [
585 (PermissionMode::ReadOnly, access_mode == AccessMode::Read),
586 (PermissionMode::WriteOnly, access_mode == AccessMode::Write),
587 (PermissionMode::ReadWrite, true),
588 ] {
589 let user = DefaultUserInfo::with_name_and_permission("test_user", permission_mode);
590 let result = checker.check_permission(user, req.clone()).unwrap();
591 assert_eq!(
592 allowed,
593 matches!(result, PermissionResp::Allow),
594 "{permission_mode:?}: {}",
595 action.name()
596 );
597 }
598 }
599 }
600
601 #[test]
602 fn test_table_target_permission_forwarding() {
603 let checker: PermissionCheckerRef = Arc::new(TargetAwarePermissionChecker);
604 let checker = Some(&checker);
605 assert!(checker.uses_table_targets());
606
607 let allowed = checker
608 .check_permission_with_table_targets(
609 crate::userinfo_by_name(None),
610 PermissionReq::Action(PROM_STORE_READ),
611 resolved_targets("allowed"),
612 )
613 .unwrap();
614 assert!(matches!(allowed, PermissionResp::Allow));
615
616 let empty = checker
617 .check_permission_with_table_targets(
618 crate::userinfo_by_name(None),
619 PermissionReq::Action(PROM_STORE_READ),
620 PermissionTableTargets::resolved(Vec::new()),
621 )
622 .unwrap();
623 assert!(matches!(empty, PermissionResp::Allow));
624
625 let rejected_operation = checker.check_permission_with_table_targets(
626 crate::userinfo_by_name(None),
627 PermissionReq::Action(PROM_STORE_WRITE),
628 PermissionTableTargets::resolved(Vec::new()),
629 );
630 assert!(matches!(
631 rejected_operation,
632 Err(Error::PermissionDenied { .. })
633 ));
634
635 let rejected = checker.check_permission_with_table_targets(
636 crate::userinfo_by_name(None),
637 PermissionReq::Action(PROM_STORE_READ),
638 resolved_targets("denied"),
639 );
640 assert!(matches!(rejected, Err(Error::PermissionDenied { .. })));
641
642 let mixed = checker.check_permission_with_table_targets(
643 crate::userinfo_by_name(None),
644 PermissionReq::Action(PROM_STORE_READ),
645 PermissionTableTargets::resolved(vec![
646 PermissionTableTarget::new("greptime", "public", "allowed"),
647 PermissionTableTarget::new("greptime", "public", "denied"),
648 ]),
649 );
650 assert!(matches!(mixed, Err(Error::PermissionDenied { .. })));
651
652 let error = checker.check_permission_with_table_targets(
653 crate::userinfo_by_name(None),
654 PermissionReq::Action(PROM_STORE_READ),
655 resolved_targets("error"),
656 );
657 assert!(matches!(error, Err(Error::InternalState { msg }) if msg == "testing"));
658
659 let no_checker: Option<&PermissionCheckerRef> = None;
660 assert!(!no_checker.uses_table_targets());
661 let allowed = no_checker
662 .check_permission_with_table_targets(
663 crate::userinfo_by_name(None),
664 PermissionReq::Action(PROM_STORE_READ),
665 PermissionTableTargets::Unresolved,
666 )
667 .unwrap();
668 assert!(matches!(allowed, PermissionResp::Allow));
669 }
670
671 #[test]
672 fn test_default_permission_checker_table_target_parity() {
673 let checker = DefaultPermissionChecker;
674 assert!(!checker.uses_table_targets());
675
676 for (permission, req) in [
677 (
678 PermissionMode::ReadOnly,
679 PermissionReq::Action(PROMQL_QUERY),
680 ),
681 (
682 PermissionMode::ReadOnly,
683 PermissionReq::Action(PROM_STORE_WRITE),
684 ),
685 (
686 PermissionMode::WriteOnly,
687 PermissionReq::Action(PROMQL_QUERY),
688 ),
689 (
690 PermissionMode::WriteOnly,
691 PermissionReq::Action(PROM_STORE_WRITE),
692 ),
693 ] {
694 let user = DefaultUserInfo::with_name_and_permission("test_user", permission);
695 let direct = checker.check_permission(user.clone(), req.clone()).unwrap();
696 let targeted = checker
697 .check_permission_with_table_targets(user, req, resolved_targets("metrics"))
698 .unwrap();
699
700 assert_eq!(
701 matches!(direct, PermissionResp::Allow),
702 matches!(targeted, PermissionResp::Allow)
703 );
704 }
705 }
706}