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