1use std::collections::BTreeSet;
2
3use crate::ast::Action;
4
5use super::model::AccessOperation;
6
7#[derive(Debug)]
9pub enum AccessPolicyLoadError {
10 Read(std::io::Error),
12 Toml(toml::de::Error),
14 Json(serde_json::Error),
16 UnsupportedExtension(String),
18}
19
20impl std::fmt::Display for AccessPolicyLoadError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 Self::Read(err) => write!(f, "failed to read access policy: {err}"),
24 Self::Toml(err) => write!(f, "failed to parse TOML access policy: {err}"),
25 Self::Json(err) => write!(f, "failed to parse JSON access policy: {err}"),
26 Self::UnsupportedExtension(extension) if extension.is_empty() => {
27 write!(f, "access policy file must use .toml or .json extension")
28 }
29 Self::UnsupportedExtension(extension) => {
30 write!(
31 f,
32 "unsupported access policy extension '.{extension}' (expected .toml or .json)"
33 )
34 }
35 }
36 }
37}
38
39impl std::error::Error for AccessPolicyLoadError {
40 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
41 match self {
42 Self::Read(err) => Some(err),
43 Self::Toml(err) => Some(err),
44 Self::Json(err) => Some(err),
45 Self::UnsupportedExtension(_) => None,
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct AccessError {
53 pub table: String,
55 pub operation: Option<AccessOperation>,
57 pub kind: AccessErrorKind,
59}
60
61impl AccessError {
62 pub(super) fn new(
63 table: String,
64 operation: Option<AccessOperation>,
65 kind: AccessErrorKind,
66 ) -> Self {
67 Self {
68 table,
69 operation,
70 kind,
71 }
72 }
73}
74
75impl std::fmt::Display for AccessError {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match &self.kind {
78 AccessErrorKind::NoPolicy => {
79 write!(f, "no access policy allows table '{}'", self.table)
80 }
81 AccessErrorKind::UnsupportedAction(action) => {
82 write!(f, "action {action:?} is not supported by access policy")
83 }
84 AccessErrorKind::OperationDenied => write!(
85 f,
86 "operation {:?} is denied on table '{}'",
87 self.operation, self.table
88 ),
89 AccessErrorKind::MissingRole { required } => write!(
90 f,
91 "table '{}' requires one of roles {:?}",
92 self.table, required
93 ),
94 AccessErrorKind::MissingScope { required } => {
95 write!(f, "table '{}' requires scopes {:?}", self.table, required)
96 }
97 AccessErrorKind::ColumnDenied { column } => write!(
98 f,
99 "column '{}' is denied for operation {:?} on table '{}'",
100 column, self.operation, self.table
101 ),
102 AccessErrorKind::WildcardProjectionDenied => write!(
103 f,
104 "wildcard projection is denied by column policy on table '{}'",
105 self.table
106 ),
107 AccessErrorKind::UnsupportedColumnExpression { context } => write!(
108 f,
109 "{} contains an expression that cannot be checked by column policy on table '{}'",
110 context, self.table
111 ),
112 AccessErrorKind::ExplicitWriteColumnsRequired => write!(
113 f,
114 "operation {:?} on table '{}' requires explicit write columns",
115 self.operation, self.table
116 ),
117 AccessErrorKind::JoinedTableColumnPolicyUnsupported => write!(
118 f,
119 "joined table '{}' has column policy that cannot be enforced in a flat join",
120 self.table
121 ),
122 AccessErrorKind::SourceTableColumnPolicyUnsupported => write!(
123 f,
124 "source table '{}' has column policy that cannot be enforced without an explicit source query",
125 self.table
126 ),
127 AccessErrorKind::AuxiliaryTableColumnPolicyUnsupported => write!(
128 f,
129 "auxiliary table '{}' has column policy that cannot be enforced in UPDATE FROM or DELETE USING",
130 self.table
131 ),
132 AccessErrorKind::CteMutationUnsupported => {
133 write!(f, "CTE relation '{}' cannot be mutated", self.table)
134 }
135 AccessErrorKind::EmptyTable => write!(f, "command has no target table"),
136 }
137 }
138}
139
140impl std::error::Error for AccessError {}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub enum AccessErrorKind {
145 NoPolicy,
147 UnsupportedAction(Action),
149 OperationDenied,
151 MissingRole {
153 required: BTreeSet<String>,
155 },
156 MissingScope {
158 required: BTreeSet<String>,
160 },
161 ColumnDenied {
163 column: String,
165 },
166 WildcardProjectionDenied,
168 UnsupportedColumnExpression {
170 context: &'static str,
172 },
173 ExplicitWriteColumnsRequired,
175 JoinedTableColumnPolicyUnsupported,
177 SourceTableColumnPolicyUnsupported,
179 AuxiliaryTableColumnPolicyUnsupported,
181 CteMutationUnsupported,
183 EmptyTable,
185}