Skip to content

Commit

Permalink
Merge pull request #59 from sebadob/impl-user-expiry-for-new-user
Browse files Browse the repository at this point in the history
Impl user expiry for new user
  • Loading branch information
sebadob committed Sep 26, 2023
2 parents e63d1ce + 2712139 commit 566fff1
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 9 deletions.
7 changes: 7 additions & 0 deletions frontend/src/components/account/AccInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@
<div class={classLabel}><b>{t.user} {t.created}:</b></div>
<span class="value">{formatDateFromTs(user.created_at)}</span>
</div>

{#if user.user_expires}
<div class={classRow}>
<div class={classLabel}><b>{t.userExpiry}:</b></div>
<span class="value">{formatDateFromTs(user.user_expires)}</span>
</div>
{/if}
</div>

<style>
Expand Down
48 changes: 47 additions & 1 deletion frontend/src/components/admin/users/UserTileAddNew.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import * as yup from "yup";
import {LANGUAGES, REGEX_NAME} from "../../../utils/constants.js";
import {globalGroupsNames, globalRolesNames} from "../../../stores/admin.js";
import {extractFormErrors} from "../../../utils/helpers.js";
import {extractFormErrors, formatUtcTsFromDateInput} from "../../../utils/helpers.js";
import Button from "$lib/Button.svelte";
import {postUser} from "../../../utils/dataFetchingAdmin.js";
import ItemTiles from "$lib/itemTiles/ItemTiles.svelte";
import Input from "$lib/inputs/Input.svelte";
import OptionSelect from "$lib/OptionSelect.svelte";
import Switch from "$lib/Switch.svelte";
export let idx = -1;
export let onSave;
Expand All @@ -18,6 +19,8 @@
let expandContainer;
let language = 'EN';
let limitLifetime = false;
let userExpires = undefined;
let formValues = {
email: '',
family_name: '',
Expand Down Expand Up @@ -54,10 +57,26 @@
let data = formValues;
data.language = language.toLowerCase();
if (limitLifetime) {
let d = formatUtcTsFromDateInput(userExpires);
if (!d) {
err = 'Invalid Date Input: User Expires';
return;
}
data.user_expires = d;
}
let res = await postUser(formValues);
if (res.ok) {
formValues = {};
expandContainer = false;
formValues = {
email: '',
family_name: '',
given_name: '',
roles: [],
groups: [],
};
onSave();
} else {
let body = await res.json();
Expand Down Expand Up @@ -144,6 +163,29 @@
searchThreshold={4}
/>
</div>

<div class="unit" style:margin-top="12px">
<div class="label font-label">
LIMIT LIFETIME
</div>
<div class="value">
<Switch bind:selected={limitLifetime}/>
</div>
</div>
{#if limitLifetime}
<Input
type="datetime-local"
step="60"
width="18rem"
bind:value={userExpires}
on:input={validateForm}
min={new Date().toISOString().split('.')[0]}
max="2099-01-01T00:00"
>
USER EXPIRES
</Input>
{/if}

<div class="btn">
<Button on:click={onSubmit} level={1}>SAVE</Button>

Expand Down Expand Up @@ -187,6 +229,10 @@
gap: .33rem;
}
.unit {
margin: 7px 5px;
}
.tiles {
margin-left: 5px;
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/admin/users/Users.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
msg = 'Error fetching users: ' + res.body.message;
} else {
let u = await res.json();
console.log(u);
users = [...u];
resUsers = [...u];
}
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/routes/users/{id}/reset/reset/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
let schema;
$: if (t) {
console.log(t);
schema = yup.object().shape({
email: yup.string().required(t.required).email(t.badFormat),
password: yup.string().required(t.required),
Expand All @@ -51,6 +50,12 @@
showCopy = true;
}
$: if (success) {
setTimeout(() => {
window.location.replace('/auth/v1/account');
}, 5000);
}
onMount(async () => {
// const policy_vals = '10, 128, 1, 1, 1, 1, 3';
const policy_vals = document.getElementsByName('rauthy-data')[0].id;
Expand Down
1 change: 1 addition & 0 deletions rauthy-main/tests/handler_users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async fn test_users() -> Result<(), Box<dyn Error>> {
// check groups sanitization
"non_existent".to_string(),
]),
user_expires: None,
};
let res = reqwest::Client::new()
.post(&url)
Expand Down
2 changes: 2 additions & 0 deletions rauthy-main/tests/yyy_handler_password_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ async fn test_password_policy() -> Result<(), Box<dyn Error>> {
language: Language::En,
roles: vec!["user".to_string()],
groups: None,
user_expires: None,
};
let mut res = reqwest::Client::new()
.post(&url)
Expand All @@ -109,6 +110,7 @@ async fn test_password_policy() -> Result<(), Box<dyn Error>> {
groups: None,
enabled: true,
email_verified: false,
user_expires: None,
};
let user_url = format!("{}/{}", url, user.id);
let mut res = reqwest::Client::new()
Expand Down
4 changes: 2 additions & 2 deletions rauthy-models/src/entity/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ impl Session {
// Returns a session by id
pub async fn find(data: &web::Data<AppState>, id: String) -> Result<Self, ErrorResponse> {
// TODO set remote lookup to true here to be able to switch to in-memory sessions store only?
let idx = format!("{}{}", IDX_SESSION, id);
let session = cache_get!(
Session,
CACHE_NAME_SESSIONS.to_string(),
id.clone(),
idx.clone(),
&data.caches.ha_cache_config,
false
)
Expand All @@ -130,7 +131,6 @@ impl Session {
.fetch_one(&data.db)
.await?;

let idx = format!("{}{}", IDX_SESSION, session.id);
cache_insert(
CACHE_NAME_SESSIONS.to_string(),
idx,
Expand Down
5 changes: 3 additions & 2 deletions rauthy-models/src/entity/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ impl User {
IDX_USERS.to_string(),
&data.caches.ha_cache_config,
&res,
AckLevel::Leader,
AckLevel::Quorum,
)
.await?;
Ok(res)
Expand Down Expand Up @@ -644,7 +644,7 @@ impl User {
if let Some(ts) = self.user_expires {
if OffsetDateTime::now_utc().unix_timestamp() > ts {
return Err(ErrorResponse::new(
ErrorResponseType::Forbidden,
ErrorResponseType::Disabled,
String::from("User has expired"),
));
}
Expand Down Expand Up @@ -722,6 +722,7 @@ impl User {
language: new_user.language,
roles,
groups,
user_expires: new_user.user_expires,
..Default::default()
};

Expand Down
3 changes: 3 additions & 0 deletions rauthy-models/src/i18n/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct I18nAccount<'a> {
roles: &'a str,
save: &'a str,
user: &'a str,
user_expiry: &'a str,
valid_email: &'a str,
valid_given_name: &'a str,
valid_family_name: &'a str,
Expand Down Expand Up @@ -91,6 +92,7 @@ impl I18nAccount<'_> {
roles: "Roles",
save: "Save",
user: "User",
user_expiry: "User Expires",
valid_email: "Valid E-Mail format",
valid_given_name: "Your given name with 2 - 32 non-special characters",
valid_family_name: "Your family name with 2 - 32 non-special characters",
Expand Down Expand Up @@ -131,6 +133,7 @@ impl I18nAccount<'_> {
roles: "Rollen",
save: "Speichern",
user: "Benutzer",
user_expiry: "Benutzer Ablauf",
valid_email: "Gültige E-Mail Adresse",
valid_given_name: "Vorname, 2 - 32 Buchstaben, keine Sonderzeichen",
valid_family_name: "Nachname, 2 - 32 Buchstaben, keine Sonderzeichen",
Expand Down
6 changes: 3 additions & 3 deletions rauthy-models/src/i18n/password_reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl I18nPasswordReset<'_> {
required: "Required",
save: "Save",
success_1: "The password has been updated successfully.",
success_2: "You can close this window now.",
success_2: "You will be redirected to your account shortly.",
}
}

Expand All @@ -63,8 +63,8 @@ impl I18nPasswordReset<'_> {
password_no_match: "Passwörter stimmen nicht überein",
required: "Notwendig",
save: "Speichern",
success_1: "Das Passwort wurde erfolgreich zurückgesetzt",
success_2: "Sie können dieses Fenster jetzt schließen",
success_1: "Das Passwort wurde erfolgreich zurückgesetzt.",
success_2: "Sie werden in Kürze zu Ihrem Account weitergeleitet.",
}
}
}
2 changes: 2 additions & 0 deletions rauthy-models/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ pub struct NewUserRequest {
/// Validation: `Vec<^[a-z0-9-_/,]{2,128}$>`
#[validate(custom(function = "validate_vec_groups"))]
pub roles: Vec<String>,
#[validate(range(min = 1672527600, max = 4070905200))]
pub user_expires: Option<i64>,
}

#[derive(Debug, Serialize, Deserialize, Validate, ToSchema)]
Expand Down
1 change: 1 addition & 0 deletions rauthy-service/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub async fn authorize(
)
})?;
user.check_enabled()?;
user.check_expired()?;

let mfa_cookie =
if let Ok(c) = WebauthnCookie::parse_validate(&req.cookie(COOKIE_MFA), &data.enc_keys) {
Expand Down

0 comments on commit 566fff1

Please sign in to comment.