Skip to main content

wekan_cli/client/admin/
login_providers.rs

1use super::{PreparedSettingsUpdate, StorageSecret};
2use crate::client::{ClientError, WekanClient};
3use reqwest::{
4    Method,
5    header::{ACCEPT, CONTENT_TYPE},
6};
7use secrecy::{ExposeSecret, SecretString};
8use serde::{Deserialize, Serialize};
9
10pub const OAUTH_PROVIDER_KEYS: [&str; 7] = [
11    "google",
12    "github",
13    "facebook",
14    "twitter",
15    "meteor-developer",
16    "weibo",
17    "meetup",
18];
19
20#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21#[serde(deny_unknown_fields)]
22pub struct ConfigSource<T> {
23    pub source: String,
24    pub value: Option<T>,
25}
26#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
27#[serde(untagged)]
28pub enum BooleanConfigValue {
29    Text(String),
30    Boolean(bool),
31}
32#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
33#[serde(
34    deny_unknown_fields,
35    rename_all(deserialize = "camelCase", serialize = "snake_case")
36)]
37pub struct SecretConfigSource {
38    pub source: String,
39    pub has_value: bool,
40}
41#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
42#[serde(deny_unknown_fields)]
43pub struct OauthProviderSource {
44    pub enabled: ConfigSource<BooleanConfigValue>,
45    pub id: ConfigSource<String>,
46    pub secret: SecretConfigSource,
47}
48/// Every entry in v12.57's oauthProviderCatalog is present, even when unset.
49#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
50#[serde(deny_unknown_fields)]
51pub struct OauthProviderSources {
52    pub google: OauthProviderSource,
53    pub github: OauthProviderSource,
54    pub facebook: OauthProviderSource,
55    pub twitter: OauthProviderSource,
56    #[serde(rename = "meteor-developer")]
57    pub meteor_developer: OauthProviderSource,
58    pub weibo: OauthProviderSource,
59    pub meetup: OauthProviderSource,
60}
61#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
62#[serde(
63    deny_unknown_fields,
64    rename_all(deserialize = "camelCase", serialize = "snake_case")
65)]
66pub struct OauthProviderSettings {
67    pub providers: OauthProviderSources,
68    pub login_style: ConfigSource<String>,
69    pub merge_existing_users: ConfigSource<BooleanConfigValue>,
70    pub allowed_email_domains: ConfigSource<String>,
71    pub passwordless: ConfigSource<BooleanConfigValue>,
72}
73#[derive(Clone, Debug, Serialize)]
74#[serde(rename_all = "camelCase")]
75pub struct UpdateOauthProviderRequest {
76    pub enabled: bool,
77    pub id: String,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub secret: Option<StorageSecret>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub login_style: Option<String>,
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub global_login_style: Option<String>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub merge_existing_users: Option<bool>,
86}
87#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
88#[serde(deny_unknown_fields)]
89pub struct PasswordlessSettings {
90    pub passwordless: ConfigSource<BooleanConfigValue>,
91}
92#[derive(Serialize)]
93struct PasswordlessRequest {
94    enabled: bool,
95}
96
97impl WekanClient {
98    pub async fn oauth_provider_settings(
99        &self,
100        token: &SecretString,
101    ) -> Result<OauthProviderSettings, ClientError> {
102        self.admin_settings(
103            Method::GET,
104            "api/admin/oauth-providers",
105            None,
106            token,
107            "OAuth provider settings",
108        )
109        .await
110    }
111    pub async fn update_oauth_provider(
112        &self,
113        provider: &str,
114        request: PreparedSettingsUpdate<UpdateOauthProviderRequest>,
115        token: &SecretString,
116    ) -> Result<OauthProviderSettings, ClientError> {
117        let mut url = self
118            .server()
119            .join("api/admin/oauth-providers")
120            .map_err(|_| ClientError::Protocol {
121                message: "could not construct OAuth provider endpoint".into(),
122                diagnostic: None,
123                success_status_received: false,
124            })?;
125        url.path_segments_mut()
126            .expect("HTTP URL supports path segments")
127            .push(provider);
128        let body = self
129            .execute_success_request_with_limit(
130                self.http
131                    .put(url)
132                    .header(ACCEPT, "application/json")
133                    .header(CONTENT_TYPE, "application/json")
134                    .bearer_auth(token.expose_secret())
135                    .body(request.body),
136                64 * 1024 * 1024,
137            )
138            .await?;
139        crate::client::transport::decode_success::<super::Object<OauthProviderSettings>>(
140            &body,
141            "OAuth provider settings update",
142            true,
143        )
144        .map(|value| value.0)
145    }
146    pub async fn update_passwordless_settings(
147        &self,
148        enabled: bool,
149        token: &SecretString,
150    ) -> Result<PasswordlessSettings, ClientError> {
151        let body = serde_json::to_vec(&PasswordlessRequest { enabled })
152            .expect("boolean request is serializable");
153        self.admin_settings(
154            Method::PUT,
155            "api/admin/passwordless",
156            Some(body),
157            token,
158            "passwordless settings update",
159        )
160        .await
161    }
162}