Skip to main content

alibi_sqlx/store/
organization_roles.rs

1use super::SqlxStore;
2use super::entities::organization_role::Model;
3use crate::model::{self, ActiveRow, SqlxModel};
4use crate::schema::AuthSchema;
5use crate::sql::Sql;
6use alibi_core::error::{AuthError, AuthResult};
7use alibi_core::store::OrganizationRoleStore;
8use alibi_core::types::{
9    CreateOrganizationRole, OrganizationRole, OrganizationRoleSelector, UpdateOrganizationRole,
10};
11use async_trait::async_trait;
12use chrono::Utc;
13use uuid::Uuid;
14
15/// Append the organization scope and role selector.
16fn scope(sql: &mut Sql, organization_id: &str, selector: &OrganizationRoleSelector) {
17    sql.push(" WHERE ");
18    sql.compare(Model::TABLE, "organizationId", " = ", organization_id);
19    sql.push(" AND ");
20    match selector {
21        OrganizationRoleSelector::Id(id) => {
22            sql.compare(Model::TABLE, "id", " = ", id.as_str());
23        }
24        OrganizationRoleSelector::Name(name) => {
25            sql.compare(Model::TABLE, "role", " = ", name.as_str());
26        }
27    }
28}
29
30#[async_trait]
31impl<S: AuthSchema> OrganizationRoleStore for SqlxStore<S> {
32    async fn create_organization_role(
33        &self,
34        data: CreateOrganizationRole,
35    ) -> AuthResult<OrganizationRole> {
36        let mut active = ActiveRow::new();
37        active.set(
38            "id",
39            self.generated_id(self.exec(), "organizationRole", "organizationRole", "id")
40                .await?
41                .unwrap_or_else(|| Uuid::new_v4().to_string()),
42        );
43        active.set("organizationId", data.organization_id);
44        active.set("role", data.role);
45        active.set("permission", serde_json::to_string(&data.permission)?);
46        active.set("createdAt", Utc::now());
47        active.set("updatedAt", None::<chrono::DateTime<Utc>>);
48        model::insert::<Model>(self.exec(), &active)
49            .await?
50            .try_into()
51    }
52    async fn get_organization_role(
53        &self,
54        organization_id: &str,
55        selector: &OrganizationRoleSelector,
56    ) -> AuthResult<Option<OrganizationRole>> {
57        let mut sql = model::select_model::<Model>(self.exec());
58        scope(&mut sql, organization_id, selector);
59        model::limit_one(&mut sql);
60        self.exec()
61            .fetch_optional::<Model>(sql)
62            .await?
63            .map(TryInto::try_into)
64            .transpose()
65    }
66    async fn list_organization_roles(
67        &self,
68        organization_id: &str,
69    ) -> AuthResult<Vec<OrganizationRole>> {
70        let mut sql = model::select_model::<Model>(self.exec());
71        sql.push(" WHERE ");
72        sql.compare(Model::TABLE, "organizationId", " = ", organization_id);
73        sql.push(" LIMIT ");
74        sql.bind(self.find_many_limit());
75        self.exec()
76            .fetch_all::<Model>(sql)
77            .await?
78            .into_iter()
79            .map(TryInto::try_into)
80            .collect()
81    }
82    async fn count_organization_roles(&self, organization_id: &str) -> AuthResult<usize> {
83        let mut sql = Sql::with(self.exec().engine(), "SELECT COUNT(*) FROM ");
84        sql.ident(Model::TABLE);
85        sql.push(" WHERE ");
86        sql.compare(Model::TABLE, "organizationId", " = ", organization_id);
87        let count = self
88            .exec()
89            .fetch_scalar::<i64>(sql)
90            .await?
91            .unwrap_or_default();
92        usize::try_from(count)
93            .map_err(|_error| AuthError::internal("Organization role count overflow"))
94    }
95    async fn has_organization_role_members(
96        &self,
97        organization_id: &str,
98        role: &str,
99    ) -> AuthResult<bool> {
100        let mut sql = self.organization_models.member.select(self.exec());
101        sql.push(" WHERE ");
102        self.organization_models.member.compare(
103            &mut sql,
104            "organizationId",
105            " = ",
106            organization_id,
107        )?;
108        sql.push(" AND ");
109        self.organization_models
110            .member
111            .compare(&mut sql, "role", " LIKE ", format!("%{role}%"))?;
112        sql.push(" LIMIT ");
113        sql.bind(self.find_many_limit());
114        let members = self
115            .organization_models
116            .member
117            .fetch_all(self.exec(), sql)
118            .await?;
119        Ok(members.iter().any(|member| {
120            member
121                .role
122                .split(',')
123                .map(str::trim)
124                .any(|name| name == role)
125        }))
126    }
127    async fn update_organization_role(
128        &self,
129        organization_id: &str,
130        selector: &OrganizationRoleSelector,
131        update: UpdateOrganizationRole,
132    ) -> AuthResult<OrganizationRole> {
133        let mut select = model::select_model::<Model>(self.exec());
134        scope(&mut select, organization_id, selector);
135        model::limit_one(&mut select);
136        let mut row = self
137            .exec()
138            .fetch_optional::<Model>(select)
139            .await?
140            .ok_or_else(|| AuthError::bad_request("Role not found"))?;
141        let updated_at = Utc::now();
142        let mut sql = Sql::with(self.exec().engine(), "UPDATE ");
143        sql.ident(Model::TABLE);
144        sql.push(" SET ");
145        sql.assign("updatedAt", Some(updated_at));
146        if let Some(role) = update.role {
147            sql.push(", ");
148            sql.assign("role", role.clone());
149            row.role = role;
150        }
151        if let Some(permission) = update.permission {
152            let permission = serde_json::to_string(&permission)?;
153            sql.push(", ");
154            sql.assign("permission", permission.clone());
155            row.permission = permission;
156        }
157        scope(&mut sql, organization_id, selector);
158        _ = self.exec().execute(sql).await?;
159        row.updated_at = Some(updated_at);
160        row.try_into()
161    }
162    async fn delete_organization_role(
163        &self,
164        organization_id: &str,
165        selector: &OrganizationRoleSelector,
166    ) -> AuthResult<bool> {
167        let mut sql = Sql::with(self.exec().engine(), "DELETE FROM ");
168        sql.ident(Model::TABLE);
169        scope(&mut sql, organization_id, selector);
170        Ok(self.exec().execute(sql).await? > 0)
171    }
172}