Skip to main content

rustfs_policy/policy/
statement.rs

1// Copyright 2024 RustFS 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 super::{
16    ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, ID, Principal, ResourceSet, Validator,
17    action::Action,
18};
19use crate::error::{Error, Result};
20use serde::{Deserialize, Serialize};
21
22#[derive(Serialize, Deserialize, Clone, Default, Debug)]
23pub struct Statement {
24    #[serde(rename = "Sid", default)]
25    pub sid: ID,
26    #[serde(rename = "Effect")]
27    pub effect: Effect,
28    #[serde(rename = "Action")]
29    pub actions: ActionSet,
30    #[serde(rename = "NotAction", default)]
31    pub not_actions: ActionSet,
32    #[serde(rename = "Resource", default)]
33    pub resources: ResourceSet,
34    #[serde(rename = "NotResource", default)]
35    pub not_resources: ResourceSet,
36    #[serde(rename = "Condition", default)]
37    pub conditions: Functions,
38}
39
40impl Statement {
41    fn is_kms(&self) -> bool {
42        for act in self.actions.iter() {
43            if matches!(act, Action::KmsAction(_)) {
44                return true;
45            }
46        }
47
48        false
49    }
50
51    fn is_admin(&self) -> bool {
52        for act in self.actions.iter() {
53            if matches!(act, Action::AdminAction(_)) {
54                return true;
55            }
56        }
57
58        false
59    }
60
61    fn is_sts(&self) -> bool {
62        for act in self.actions.iter() {
63            if matches!(act, Action::StsAction(_)) {
64                return true;
65            }
66        }
67
68        false
69    }
70
71    pub fn is_allowed(&self, args: &Args) -> bool {
72        let check = 'c: {
73            if (!self.actions.is_match(&args.action) && !self.actions.is_empty()) || self.not_actions.is_match(&args.action) {
74                break 'c false;
75            }
76
77            let mut resource = String::from(args.bucket);
78            if !args.object.is_empty() {
79                if !args.object.starts_with('/') {
80                    resource.push('/');
81                }
82
83                resource.push_str(args.object);
84            } else {
85                resource.push('/');
86            }
87
88            if self.is_kms() && (resource == "/" || self.resources.is_empty()) {
89                break 'c self.conditions.evaluate(args.conditions);
90            }
91
92            if !self.resources.is_match(&resource, args.conditions) && !self.is_admin() && !self.is_sts() {
93                break 'c false;
94            }
95
96            self.conditions.evaluate(args.conditions)
97        };
98
99        self.effect.is_allowed(check)
100    }
101}
102
103impl Validator for Statement {
104    type Error = Error;
105    fn is_valid(&self) -> Result<()> {
106        self.effect.is_valid()?;
107        // check sid
108        self.sid.is_valid()?;
109
110        if self.actions.is_empty() && self.not_actions.is_empty() {
111            return Err(IamError::NonAction.into());
112        }
113
114        if self.resources.is_empty() {
115            return Err(IamError::NonResource.into());
116        }
117
118        self.actions.is_valid()?;
119        self.not_actions.is_valid()?;
120        self.resources.is_valid()?;
121
122        Ok(())
123    }
124}
125
126impl PartialEq for Statement {
127    fn eq(&self, other: &Self) -> bool {
128        self.effect == other.effect
129            && self.actions == other.actions
130            && self.not_actions == other.not_actions
131            && self.resources == other.resources
132            && self.conditions == other.conditions
133    }
134}
135
136#[derive(Debug, Deserialize, Serialize, Default, Clone)]
137#[serde(rename_all = "PascalCase", default)]
138pub struct BPStatement {
139    #[serde(rename = "Sid", default)]
140    pub sid: ID,
141    #[serde(rename = "Effect")]
142    pub effect: Effect,
143    #[serde(rename = "Principal")]
144    pub principal: Principal,
145    #[serde(rename = "Action")]
146    pub actions: ActionSet,
147    #[serde(rename = "NotAction", default)]
148    pub not_actions: ActionSet,
149    #[serde(rename = "Resource", default)]
150    pub resources: ResourceSet,
151    #[serde(rename = "NotResource", default)]
152    pub not_resources: ResourceSet,
153    #[serde(rename = "Condition", default)]
154    pub conditions: Functions,
155}
156
157impl BPStatement {
158    pub fn is_allowed(&self, args: &BucketPolicyArgs) -> bool {
159        let check = 'c: {
160            if !self.principal.is_match(args.account) {
161                break 'c false;
162            }
163
164            if (!self.actions.is_match(&args.action) && !self.actions.is_empty()) || self.not_actions.is_match(&args.action) {
165                break 'c false;
166            }
167
168            let mut resource = String::from(args.bucket);
169            if !args.object.is_empty() {
170                if !args.object.starts_with('/') {
171                    resource.push('/');
172                }
173
174                resource.push_str(args.object);
175            } else {
176                resource.push('/');
177            }
178
179            if !self.resources.is_empty() && !self.resources.is_match(&resource, args.conditions) {
180                break 'c false;
181            }
182
183            if !self.not_resources.is_empty() && self.not_resources.is_match(&resource, args.conditions) {
184                break 'c false;
185            }
186
187            self.conditions.evaluate(args.conditions)
188        };
189
190        self.effect.is_allowed(check)
191    }
192}
193
194impl Validator for BPStatement {
195    type Error = Error;
196    fn is_valid(&self) -> Result<()> {
197        self.effect.is_valid()?;
198        // check sid
199        self.sid.is_valid()?;
200
201        self.principal.is_valid()?;
202
203        if self.actions.is_empty() && self.not_actions.is_empty() {
204            return Err(IamError::NonAction.into());
205        }
206
207        if self.resources.is_empty() {
208            return Err(IamError::NonResource.into());
209        }
210
211        self.actions.is_valid()?;
212        self.not_actions.is_valid()?;
213        self.resources.is_valid()?;
214
215        Ok(())
216    }
217}