sofie_identity/tokens/
factory.rs1use sofie::auth::BearerToken;
2use sofie::core::Instant;
3use sofie::db::{Outcome, Plan, Reply, Stamp};
4use sofie::http::Area;
5use sofie::prelude::*;
6
7use super::{AccessToken, AccessTokenId};
8use crate::AccountId;
9use crate::membership::held;
10use crate::role_grant::factory::refuse_unless_permission;
11
12pub struct Issued {
14 token: AccessToken,
15 secret: BearerToken,
16}
17
18impl Issued {
19 #[must_use]
20 pub fn token(&self) -> &AccessToken {
21 &self.token
22 }
23
24 #[must_use]
25 pub fn secret(&self) -> &BearerToken {
26 &self.secret
27 }
28
29 #[cfg(feature = "token-screens")]
30 pub(crate) fn revealed(mut self, sealing: &sofie::http::Sealing) -> Result<Self> {
31 let sealed = sealing.seal(super::REVEALING, self.secret.expose().as_bytes())?;
32 self.token.revealing = Some(sealed);
33 Ok(self)
34 }
35}
36
37impl Outcome for Issued {
38 fn plan(&self, plan: &mut Plan<'_>) -> Result<()> {
39 self.token.plan(plan)
40 }
41
42 fn committed(&mut self, stamp: Stamp) {
43 self.token.committed(stamp);
44 }
45
46 fn reply(self) -> Result<Reply> {
47 Ok(Reply::Json(sofie::serde_json::json!({
48 "id": self.token.id().uuid().to_string(),
49 "token": self.secret.expose(),
50 })))
51 }
52}
53
54pub async fn issue<A: Area>(
56 read: &Read,
57 account: AccountId,
58 scopes: &[&str],
59 expires_at: Instant,
60) -> Result<Issued> {
61 for scope in scopes {
62 refuse_unless_permission("scopes", scope)?;
63 }
64 if held(read, account, A::NAME).await?.is_none() {
65 return Err(Error::Rule(format!(
66 "a token for area `{}` needs its account to be a member there",
67 A::NAME
68 )));
69 }
70 let secret = BearerToken::generate()?;
71 let token = AccessToken {
72 meta: Meta::new()?,
73 account,
74 area: A::NAME.to_owned(),
75 digest: secret.digest(),
76 scopes: scopes.join(" "),
77 expires_at,
78 last_used_at: None,
79 revealing: None,
80 };
81 Ok(Issued { token, secret })
82}
83
84pub async fn revoke(read: &Read, account: AccountId, token: AccessTokenId) -> Result<AccessToken> {
85 let mut held = AccessToken::query()
86 .where_(AccessToken::ID.eq(token))
87 .where_(AccessToken::ACCOUNT.eq(account))
88 .first(read)
89 .await?
90 .ok_or(Error::NotFound)?;
91 held.delete();
92 Ok(held)
93}
94
95pub async fn list(read: &Read, account: AccountId) -> Result<Vec<AccessToken>> {
97 AccessToken::query()
98 .where_(AccessToken::ACCOUNT.eq(account))
99 .latest()
100 .all(read)
101 .await
102}