CREATE TYPE account_permission AS ENUM (
'profile-read',
'profile-write',
'profile-delete',
'hosts-read',
'hosts-write',
'hosts-create',
'hosts-delete',
'scripts-read',
'scripts-write',
'scripts-create',
'scripts-delete',
'services-activate'
);
CREATE TABLE IF NOT EXISTS account_permissions (
account_id bigint,
permission account_permission NOT NULL,
CONSTRAINT fk_account FOREIGN KEY (account_id)
REFERENCES accounts(id)
);
And on the Rust side:
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, sqlx::Type, Deserialize, Serialize)]
#[sqlx(type_name = "account_permission", rename_all = "kebab-case")]
pub enum Permission {
ProfileRead,
ProfileWrite,
ProfileDelete,
HostsRead,
HostsWrite,
HostsCreate,
HostsDelete,
ScriptsRead,
ScriptsWrite,
ScriptsCreate,
ScriptsDelete,
ServicesActivate,
}
impl AuthzBackend for Database {
type Permission = Permission;
async fn get_user_permissions(
&self,
user: &Self::User,
) -> Result<HashSet<Self::Permission>, Self::Error> {
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct AccountPermissions {
permission: Permission,
}
let permissions: Vec<AccountPermissions> =
sqlx::query_as("SELECT DISTINCT permission FROM account_permissions WHERE account_id = $1")
.bind(user.id)
.fetch_all(&self.db)
.await?;
Ok(permissions.into_iter().map(|p| p.permission).collect())
}
}
Looks simple, yes, but getting here from SQLx's permissions example was non-trivial.
Why not use their method, of having a permissions table with string keys? Because that's bad typing, and would involve a join, and I hate it.