Skip to main content

openstack_keystone_core/assignment/
service.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//     http://www.apache.org/licenses/LICENSE-2.0
5//
6// Unless required by applicable law or agreed to in writing, software
7// distributed under the License is distributed on an "AS IS" BASIS,
8// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9// See the License for the specific language governing permissions and
10// limitations under the License.
11//
12// SPDX-License-Identifier: Apache-2.0
13//! # Assignments provider
14use async_trait::async_trait;
15use std::sync::Arc;
16use validator::Validate;
17
18use crate::assignment::{AssignmentProviderError, backend::AssignmentBackend, types::*};
19use crate::config::Config;
20use crate::identity::IdentityApi;
21use crate::keystone::ServiceState;
22use crate::plugin_manager::PluginManagerApi;
23use crate::resource::ResourceApi;
24use crate::revoke::{RevokeApi, types::RevocationEventCreate};
25
26pub struct AssignmentService {
27    backend_driver: Arc<dyn AssignmentBackend>,
28}
29
30impl AssignmentService {
31    pub fn new<P: PluginManagerApi>(
32        config: &Config,
33        plugin_manager: &P,
34    ) -> Result<Self, AssignmentProviderError> {
35        let backend_driver = plugin_manager
36            .get_assignment_backend(config.assignment.driver.clone())?
37            .clone();
38        Ok(Self { backend_driver })
39    }
40}
41
42#[async_trait]
43impl AssignmentApi for AssignmentService {
44    /// Create assignment grant.
45    #[tracing::instrument(level = "info", skip(self, state))]
46    async fn create_grant(
47        &self,
48        state: &ServiceState,
49        grant: AssignmentCreate,
50    ) -> Result<Assignment, AssignmentProviderError> {
51        self.backend_driver.create_grant(state, grant).await
52    }
53
54    /// List role assignments
55    #[tracing::instrument(level = "info", skip(self, state))]
56    async fn list_role_assignments(
57        &self,
58        state: &ServiceState,
59        params: &RoleAssignmentListParameters,
60    ) -> Result<Vec<Assignment>, AssignmentProviderError> {
61        params.validate()?;
62        let mut request = RoleAssignmentListForMultipleActorTargetParametersBuilder::default();
63        let mut actors: Vec<String> = Vec::new();
64        let mut targets: Vec<RoleAssignmentTarget> = Vec::new();
65        if let Some(role_id) = &params.role_id {
66            request.role_id(role_id);
67        }
68        if let Some(uid) = &params.user_id {
69            actors.push(uid.into());
70        }
71        if let Some(true) = &params.effective
72            && let Some(uid) = &params.user_id
73        {
74            let users = state
75                .provider
76                .get_identity_provider()
77                .list_groups_of_user(state, uid)
78                .await?;
79            actors.extend(users.into_iter().map(|x| x.id));
80        };
81        if let Some(val) = &params.project_id {
82            targets.push(RoleAssignmentTarget {
83                id: val.clone(),
84                r#type: RoleAssignmentTargetType::Project,
85                inherited: Some(false),
86            });
87            if let Some(parents) = state
88                .provider
89                .get_resource_provider()
90                .get_project_parents(state, val)
91                .await?
92            {
93                parents.iter().for_each(|parent_project| {
94                    targets.push(RoleAssignmentTarget {
95                        id: parent_project.id.clone(),
96                        r#type: RoleAssignmentTargetType::Project,
97                        inherited: Some(true),
98                    });
99                });
100            }
101        } else if let Some(val) = &params.domain_id {
102            targets.push(RoleAssignmentTarget {
103                id: val.clone(),
104                r#type: RoleAssignmentTargetType::Domain,
105                inherited: Some(false),
106            });
107        } else if let Some(val) = &params.system_id {
108            targets.push(RoleAssignmentTarget {
109                id: val.clone(),
110                r#type: RoleAssignmentTargetType::System,
111                inherited: Some(false),
112            })
113        }
114        request.targets(targets);
115        request.actors(actors);
116        self.backend_driver
117            .list_assignments_for_multiple_actors_and_targets(state, &request.build()?)
118            .await
119    }
120
121    /// Revoke grant
122    #[tracing::instrument(level = "info", skip(self, state))]
123    async fn revoke_grant(
124        &self,
125        state: &ServiceState,
126        grant: Assignment,
127    ) -> Result<(), AssignmentProviderError> {
128        // Call backend with reference (no move)
129        self.backend_driver.revoke_grant(state, &grant).await?;
130
131        // Determine user_id or group_id
132        let user_id = match &grant.r#type {
133            AssignmentType::UserDomain
134            | AssignmentType::UserProject
135            | AssignmentType::UserSystem => Some(grant.actor_id.clone()),
136
137            AssignmentType::GroupDomain
138            | AssignmentType::GroupProject
139            | AssignmentType::GroupSystem => None,
140        };
141
142        // Determine project_id or domain_id
143        let (project_id, domain_id) = match &grant.r#type {
144            AssignmentType::UserProject | AssignmentType::GroupProject => {
145                (Some(grant.target_id.clone()), None)
146            }
147            AssignmentType::UserDomain | AssignmentType::GroupDomain => {
148                (None, Some(grant.target_id.clone()))
149            }
150            AssignmentType::UserSystem | AssignmentType::GroupSystem => (None, None),
151        };
152
153        let revocation_event = RevocationEventCreate {
154            domain_id,
155            project_id,
156            user_id,
157            role_id: Some(grant.role_id.clone()),
158            trust_id: None,
159            consumer_id: None,
160            access_token_id: None,
161            issued_before: chrono::Utc::now(),
162            expires_at: None,
163            audit_id: None,
164            audit_chain_id: None,
165            revoked_at: chrono::Utc::now(),
166        };
167
168        state
169            .provider
170            .get_revoke_provider()
171            .create_revocation_event(state, revocation_event)
172            .await?;
173
174        Ok(())
175    }
176}