Skip to content

Commit e9935a3

Browse files
committed
wip: add cust_email_mapping support
1 parent f83a667 commit e9935a3

File tree

12 files changed

+110
-20
lines changed

12 files changed

+110
-20
lines changed

frontend/src/api/types/clients.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export interface UpdateClientRequest {
6565
backchannel_logout_uri?: string,
6666
/// Validation: PATTERN_GROUP
6767
restrict_group_prefix?: string,
68+
/// Validation: PATTERN_ATTR
69+
cust_email_mapping?: string,
6870
scim?: ScimClientRequestResponse,
6971
}
7072

@@ -95,6 +97,7 @@ export interface ClientResponse {
9597
backchannel_logout_uri?: string,
9698
restrict_group_prefix?: string,
9799
scim?: ScimClientRequestResponse,
100+
cust_email_mapping?: string,
98101
}
99102

100103
export interface ClientSecretResponse {

frontend/src/lib/admin/clients/ClientConfig.svelte

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@
3333
client = $bindable(),
3434
clients,
3535
scopesAll,
36+
attrsAll,
3637
onSave,
3738
}: {
3839
client: ClientResponse,
3940
clients: ClientResponse[],
4041
scopesAll: string[],
42+
attrsAll: string[],
4143
onSave: () => void,
4244
} = $props();
4345
46+
let attrsWithNone = $state(['None'].concat(attrsAll));
47+
4448
let t = useI18n();
4549
let ta = useI18nAdmin();
4650
@@ -59,6 +63,7 @@
5963
let postLogoutRedirectURIs: string[] = $state(client.post_logout_redirect_uris ? Array.from(client.post_logout_redirect_uris) : []);
6064
let backchannel_logout_uri: string = $state(client.backchannel_logout_uri || '');
6165
let restrict_group_prefix: string = $state(client.restrict_group_prefix || '');
66+
let cust_email_mapping: string = $state(client.cust_email_mapping || 'None');
6267
6368
let scimEnabled = $state(client.scim !== undefined);
6469
let scim: ScimClientRequestResponse = $state({
@@ -117,6 +122,7 @@
117122
origins = client.allowed_origins ? Array.from(client.allowed_origins) : [];
118123
redirectURIs = Array.from(client.redirect_uris);
119124
postLogoutRedirectURIs = client.post_logout_redirect_uris ? Array.from(client.post_logout_redirect_uris) : [];
125+
cust_email_mapping = client.cust_email_mapping || 'None';
120126
121127
flows.authorizationCode = client.flows_enabled.includes('authorization_code');
122128
flows.clientCredentials = client.flows_enabled.includes('client_credentials');
@@ -204,6 +210,7 @@
204210
contacts: contacts.length > 0 ? contacts : undefined,
205211
backchannel_logout_uri: backchannel_logout_uri || undefined,
206212
restrict_group_prefix: restrict_group_prefix || undefined,
213+
cust_email_mapping: cust_email_mapping != 'None' ? cust_email_mapping : undefined,
207214
}
208215
209216
if (flows.authorizationCode) {
@@ -311,6 +318,14 @@
311318
width={inputWidth}
312319
pattern={PATTERN_GROUP}
313320
/>
321+
<div class="flex gap-05">
322+
Todo
323+
<Options
324+
ariaLabel={ta.attrs.name}
325+
options={attrsWithNone}
326+
bind:value={cust_email_mapping}
327+
/>
328+
</div>
314329

315330
<p class="mb-0"><b>Authentication Flows</b></p>
316331
<InputCheckbox ariaLabel="authorization_code" bind:checked={flows.authorizationCode}>

frontend/src/lib/admin/clients/ClientDetails.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
client,
1313
clients,
1414
scopesAll,
15+
attrsAll,
1516
onSave,
1617
}: {
1718
client: ClientResponse,
1819
clients: ClientResponse[],
1920
scopesAll: string[],
21+
attrsAll: string[],
2022
onSave: () => void,
2123
} = $props();
2224
@@ -49,7 +51,7 @@
4951

5052
<div class="details">
5153
{#if selected === ta.nav.config}
52-
<ClientConfig {client} {clients} {scopesAll} {onSave}/>
54+
<ClientConfig {client} {clients} {scopesAll} {attrsAll} {onSave}/>
5355
{:else if selected === 'Secret'}
5456
<ClientSecret {client}/>
5557
{:else if selected === 'Branding'}

frontend/src/lib/admin/clients/Clients.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import type {ClientResponse} from "$api/types/clients.ts";
1313
import ClientAddNew from "$lib5/admin/clients/ClientAddNew.svelte";
1414
import ClientDetails from "$lib5/admin/clients/ClientDetails.svelte";
15+
import type { UserAttrConfigResponse } from "$api/types/user_attrs";
1516
1617
let ta = useI18nAdmin();
1718
@@ -23,6 +24,7 @@
2324
let client: undefined | ClientResponse = $state();
2425
let cid = useParam('cid');
2526
let scopesAll: string[] = $state([]);
27+
let attrsAll: string[] = $state([]);
2628
2729
const searchOptions = ['ID'];
2830
let searchOption = $state(searchOptions[0]);
@@ -32,6 +34,7 @@
3234
onMount(() => {
3335
fetchClients();
3436
fetchScopes();
37+
fetchAttrs();
3538
});
3639
3740
$effect(() => {
@@ -65,6 +68,14 @@
6568
}
6669
}
6770
71+
async function fetchAttrs() {
72+
let res = await fetchGet<UserAttrConfigResponse>('/auth/v1/users/attr');
73+
if (res.body) {
74+
attrsAll = res.body.values.map((a) => a.name);
75+
} else {
76+
err = res.error?.message || 'Error';
77+
}
78+
}
6879
function onChangeOrder(option: string, direction: 'up' | 'down') {
6980
let up = direction === 'up';
7081
if (option === orderOptions[0]) {
@@ -127,7 +138,7 @@
127138

128139
<div id="groups">
129140
{#if client}
130-
<ClientDetails {client} {clients} {scopesAll} {onSave}/>
141+
<ClientDetails {client} {clients} {scopesAll} {attrsAll} {onSave}/>
131142
{/if}
132143
</div>
133144
</ContentAdmin>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ALTER TABLE clients
2+
ADD cust_email_mapping TEXT
3+
REFERENCES user_attr_config
4+
ON UPDATE CASCADE ON DELETE CASCADE;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ALTER TABLE clients
2+
ADD cust_email_mapping TEXT
3+
REFERENCES user_attr_config
4+
ON UPDATE ON DELETE CASCADE;

src/api_types/src/clients.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::cust_validation::*;
22
use crate::oidc::JwkKeyPairAlg;
33
use rauthy_common::regex::{
4-
RE_CLIENT_ID_EPHEMERAL, RE_CLIENT_NAME, RE_GROUPS, RE_LOWERCASE, RE_SCOPE_SPACE,
4+
RE_ATTR, RE_CLIENT_ID_EPHEMERAL, RE_CLIENT_NAME, RE_GROUPS, RE_LOWERCASE, RE_SCOPE_SPACE,
55
RE_TOKEN_ENDPOINT_AUTH_METHOD, RE_URI,
66
};
77
use serde::{Deserialize, Serialize};
@@ -194,6 +194,8 @@ pub struct UpdateClientRequest {
194194
/// Validation: `^[a-zA-Z0-9-_/,:*\\s]{2,64}$`
195195
#[validate(regex(path = "*RE_GROUPS", code = "^[a-zA-Z0-9-_/,:*\\s]{2,64}$"))]
196196
pub restrict_group_prefix: Option<String>,
197+
#[validate(regex(path = "*RE_ATTR", code = "^[a-z0-9-_/]{2,32}$"))]
198+
pub cust_email_mapping: Option<String>,
197199
#[validate(nested)]
198200
pub scim: Option<ScimClientRequestResponse>,
199201
}
@@ -254,6 +256,8 @@ pub struct ClientResponse {
254256
pub restrict_group_prefix: Option<String>,
255257
#[serde(skip_serializing_if = "Option::is_none")]
256258
pub scim: Option<ScimClientRequestResponse>,
259+
#[serde(skip_serializing_if = "Option::is_none")]
260+
pub cust_email_mapping: Option<String>,
257261
}
258262

259263
#[derive(Serialize, ToSchema)]

src/models/src/entity/clients.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ SET name = $1, enabled = $2, confidential = $3, secret = $4, secret_kid = $5, re
3838
post_logout_redirect_uris = $7, allowed_origins = $8, flows_enabled = $9, access_token_alg = $10,
3939
id_token_alg = $11, auth_code_lifetime = $12, access_token_lifetime = $13, scopes = $14,
4040
default_scopes = $15, challenge = $16, force_mfa= $17, client_uri = $18, contacts = $19,
41-
backchannel_logout_uri = $20, restrict_group_prefix = $21
42-
WHERE id = $22"#;
41+
backchannel_logout_uri = $20, restrict_group_prefix = $21, cust_email_mapping = $22
42+
WHERE id = $23"#;
4343

4444
/**
4545
# OIDC Client
@@ -77,6 +77,7 @@ pub struct Client {
7777
pub contacts: Option<String>,
7878
pub backchannel_logout_uri: Option<String>,
7979
pub restrict_group_prefix: Option<String>,
80+
pub cust_email_mapping: Option<String>,
8081
}
8182

8283
impl Debug for Client {
@@ -87,7 +88,8 @@ impl Debug for Client {
8788
redirect_uris: {}, post_logout_redirect_uris: {:?}, allowed_origins: {:?}, \
8889
flows_enabled: {}, access_token_alg: {}, id_token_alg: {}, auth_code_lifetime: {}, \
8990
access_token_lifetime: {}, scopes: {}, default_scopes: {}, challenge: {:?}, force_mfa: {}, \
90-
client_uri: {:?}, contacts: {:?}, backchannel_logout_uri: {:?}, restrict_group_prefix: {:?} \
91+
client_uri: {:?}, contacts: {:?}, backchannel_logout_uri: {:?}, restrict_group_prefix: {:?}, \
92+
cust_email_mapping: {:?}
9193
}}",
9294
self.id,
9395
self.name,
@@ -109,6 +111,7 @@ impl Debug for Client {
109111
self.contacts,
110112
self.backchannel_logout_uri,
111113
self.restrict_group_prefix,
114+
self.cust_email_mapping
112115
)
113116
}
114117
}
@@ -138,6 +141,7 @@ impl From<tokio_postgres::Row> for Client {
138141
contacts: row.get("contacts"),
139142
backchannel_logout_uri: row.get("backchannel_logout_uri"),
140143
restrict_group_prefix: row.get("restrict_group_prefix"),
144+
cust_email_mapping: row.get("cust_email_mapping"),
141145
}
142146
}
143147
}
@@ -165,9 +169,9 @@ impl Client {
165169
INSERT INTO clients (id, name, enabled, confidential, secret, secret_kid, redirect_uris,
166170
post_logout_redirect_uris, allowed_origins, flows_enabled, access_token_alg, id_token_alg,
167171
auth_code_lifetime, access_token_lifetime, scopes, default_scopes, challenge, force_mfa,
168-
client_uri, contacts, backchannel_logout_uri, restrict_group_prefix)
172+
client_uri, contacts, backchannel_logout_uri, restrict_group_prefix, cust_email_mapping)
169173
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
170-
$18, $19, $20, $21, $22)"#;
174+
$18, $19, $20, $21, $22, $23)"#;
171175

172176
if is_hiqlite() {
173177
DB::hql()
@@ -195,7 +199,8 @@ $18, $19, $20, $21, $22)"#;
195199
&client.client_uri,
196200
&client.contacts,
197201
&client.backchannel_logout_uri,
198-
&client.restrict_group_prefix
202+
&client.restrict_group_prefix,
203+
&client.cust_email_mapping
199204
),
200205
)
201206
.await?;
@@ -225,6 +230,7 @@ $18, $19, $20, $21, $22)"#;
225230
&client.contacts,
226231
&client.backchannel_logout_uri,
227232
&client.restrict_group_prefix,
233+
&client.cust_email_mapping,
228234
],
229235
)
230236
.await?;
@@ -250,9 +256,9 @@ $18, $19, $20, $21, $22)"#;
250256
INSERT INTO clients (id, name, enabled, confidential, secret, secret_kid, redirect_uris,
251257
post_logout_redirect_uris, allowed_origins, flows_enabled, access_token_alg, id_token_alg,
252258
auth_code_lifetime, access_token_lifetime, scopes, default_scopes, challenge, force_mfa,
253-
client_uri, contacts, backchannel_logout_uri, restrict_group_prefix)
259+
client_uri, contacts, backchannel_logout_uri, restrict_group_prefix, cust_email_mapping)
254260
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
255-
$21, $22)"#;
261+
$21, $22, $23)"#;
256262
let sql_2 = r#"
257263
INSERT INTO
258264
clients_dyn (id, created, registration_token, token_endpoint_auth_method)
@@ -285,7 +291,8 @@ VALUES ($1, $2, $3, $4)"#;
285291
&client.client_uri,
286292
&client.contacts,
287293
&client.backchannel_logout_uri,
288-
&client.restrict_group_prefix
294+
&client.restrict_group_prefix,
295+
&client.cust_email_mapping
289296
),
290297
),
291298
(
@@ -329,6 +336,7 @@ VALUES ($1, $2, $3, $4)"#;
329336
&client.contacts,
330337
&client.backchannel_logout_uri,
331338
&client.restrict_group_prefix,
339+
&client.cust_email_mapping,
332340
],
333341
)
334342
.await?;
@@ -641,6 +649,7 @@ VALUES ($1, $2, $3, $4)"#;
641649
contacts,
642650
backchannel_logout_uri,
643651
&self.restrict_group_prefix,
652+
&self.cust_email_mapping,
644653
self.id.clone()
645654
),
646655
)
@@ -670,6 +679,7 @@ VALUES ($1, $2, $3, $4)"#;
670679
&contacts,
671680
&backchannel_logout_uri,
672681
&self.restrict_group_prefix,
682+
&self.cust_email_mapping,
673683
&self.id,
674684
],
675685
)
@@ -1472,6 +1482,7 @@ impl Client {
14721482
sync_groups: scim.sync_groups,
14731483
group_sync_prefix: scim.group_sync_prefix,
14741484
}),
1485+
cust_email_mapping: self.cust_email_mapping,
14751486
}
14761487
}
14771488
}
@@ -1517,6 +1528,7 @@ impl From<EphemeralClientRequest> for Client {
15171528
contacts: value.contacts.map(|c| c.join(",")),
15181529
backchannel_logout_uri: None,
15191530
restrict_group_prefix: None,
1531+
cust_email_mapping: None,
15201532
}
15211533
}
15221534
}
@@ -1556,6 +1568,7 @@ impl Default for Client {
15561568
contacts: None,
15571569
backchannel_logout_uri: None,
15581570
restrict_group_prefix: None,
1571+
cust_email_mapping: None,
15591572
}
15601573
}
15611574
}
@@ -1765,6 +1778,7 @@ mod tests {
17651778
contacts: Some("[email protected],@alfred:matrix.org".to_string()),
17661779
backchannel_logout_uri: None,
17671780
restrict_group_prefix: None,
1781+
cust_email_mapping: None,
17681782
};
17691783

17701784
assert_eq!(client.get_access_token_alg().unwrap(), JwkKeyPairAlg::EdDSA);

src/models/src/migration/anti_lockout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub async fn anti_lockout() -> Result<(), ErrorResponse> {
5959
contacts: vars.email.rauthy_admin_email.clone(),
6060
backchannel_logout_uri: None,
6161
restrict_group_prefix: None,
62+
cust_email_mapping: None,
6263
};
6364
debug!("Rauthy client anti-lockout: {:?}", rauthy);
6465

src/service/src/client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub async fn update_client(
5656
client.client_uri = client_req.client_uri;
5757
client.backchannel_logout_uri = client_req.backchannel_logout_uri;
5858
client.restrict_group_prefix = client_req.restrict_group_prefix;
59+
client.cust_email_mapping = client_req.cust_email_mapping;
5960

6061
client.save().await?;
6162

0 commit comments

Comments
 (0)