Skip to content

Commit

Permalink
Various forgotten fixups necessary for emails being nullable (#667)
Browse files Browse the repository at this point in the history
  • Loading branch information
myieye authored Apr 4, 2024
1 parent 2b81925 commit 457a7e5
Show file tree
Hide file tree
Showing 16 changed files with 52 additions and 29 deletions.
2 changes: 1 addition & 1 deletion backend/LexBoxApi/GraphQL/CustomTypes/MeDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public class MeDto
{
public required Guid Id { get; set; }
public required string Name { get; set; }
public required string Email { get; set; }
public required string? Email { get; set; }
public required string Locale { get; set; }
}
17 changes: 11 additions & 6 deletions backend/LexBoxApi/GraphQL/UserMutations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ namespace LexBoxApi.GraphQL;
[MutationType]
public class UserMutations
{
public record ChangeUserAccountDataInput(Guid UserId, [property: EmailAddress] string Email, string Name);
public record ChangeUserAccountBySelfInput(Guid UserId, string Email, string Name, string Locale)
public record ChangeUserAccountDataInput(Guid UserId, [property: EmailAddress] string? Email, string Name);
public record ChangeUserAccountBySelfInput(Guid UserId, string? Email, string Name, string Locale)
: ChangeUserAccountDataInput(UserId, Email, Name);
public record ChangeUserAccountByAdminInput(Guid UserId, string Email, string Name, UserRole Role)
public record ChangeUserAccountByAdminInput(Guid UserId, string? Email, string Name, UserRole Role)
: ChangeUserAccountDataInput(UserId, Email, Name);

[Error<NotFoundException>]
Expand Down Expand Up @@ -85,9 +85,14 @@ EmailService emailService
permissionService.AssertIsAdmin();
if (user.Id != loggedInContext.User.Id)
{
var wasAdmin = user.IsAdmin;
user.IsAdmin = adminInput.Role == UserRole.admin;
wasPromotedToAdmin = user.IsAdmin && !wasAdmin;
if (!user.IsAdmin && adminInput.Role == UserRole.admin)
{
if (!user.EmailVerified)
{
throw new ValidationException("User must have a verified email address to be promoted to admin");
}
wasPromotedToAdmin = user.IsAdmin = true;
}
}
}
else if (input is ChangeUserAccountBySelfInput selfInput)
Expand Down
1 change: 1 addition & 0 deletions backend/LexCore/Auth/LexAuthConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public static class LexAuthConstants
{
public const string RoleClaimType = "role";
public const string EmailClaimType = "email";
public const string UsernameClaimType = "user";
public const string NameClaimType = "name";
public const string IdClaimType = "sub";
public const string AudienceClaimType = "aud";
Expand Down
4 changes: 4 additions & 0 deletions backend/LexCore/Auth/LexAuthUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public LexAuthUser(User user)
{
Id = user.Id;
Email = user.Email;
Username = user.Username;
Role = user.IsAdmin ? UserRole.admin : UserRole.user;
Name = user.Name;
UpdatedDate = user.UpdatedDate.ToUnixTimeSeconds();
Expand All @@ -110,6 +111,9 @@ public LexAuthUser(User user)
[JsonPropertyName(LexAuthConstants.EmailClaimType)]
public string? Email { get; set; }

[JsonPropertyName(LexAuthConstants.UsernameClaimType)]
public string? Username { get; set; }

[JsonPropertyName(LexAuthConstants.NameClaimType)]
public required string Name { get; set; }

Expand Down
7 changes: 4 additions & 3 deletions frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ type LexAuthUser {
updatedDate: Long!
audience: LexboxAudience!
email: String
username: String
name: String!
role: UserRole!
isAdmin: Boolean!
Expand All @@ -148,7 +149,7 @@ type LexAuthUser {
type MeDto {
id: UUID!
name: String!
email: String!
email: String
locale: String!
}

Expand Down Expand Up @@ -348,14 +349,14 @@ input ChangeProjectNameInput {
input ChangeUserAccountByAdminInput {
role: UserRole!
userId: UUID!
email: String!
email: String
name: String!
}

input ChangeUserAccountBySelfInput {
locale: String!
userId: UUID!
email: String!
email: String
name: String!
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/modals/FormModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
modal.close();
}
export function form(): Readable<Readonly<FormType>> {
export function form(): Readable<FormType> {
return superForm.form;
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/email/EmailVerificationStatus.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
</div>
<span class="i-mdi-email-heart-outline" />
</div>
{:else}
{:else if user.email}
<div class="alert alert-warning" transition:slide|local>
<span>{$t('account_settings.verify_email.please_verify')}</span>
<Button loading={sendingVerificationEmail} on:click={sendVerificationEmail}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/forms/Input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
export let id = randomFormId();
export let label: string;
export let description: string | undefined = undefined;
export let value: string | undefined = undefined;
export let value: string | undefined | null = undefined;
export let type: 'text' | 'email' | 'password' = 'text';
export let autofocus: true | undefined = undefined;
export let readonly = false;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/forms/PlainInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
let input: HTMLInputElement;
export let id = randomFormId();
export let value: string | undefined = undefined;
export let value: string | undefined | null = undefined;
export let type: 'text' | 'email' | 'password' = 'text';
export let autofocus: true | undefined = undefined;
export let readonly = false;
Expand All @@ -21,7 +21,7 @@
export let autocomplete: 'new-password' | 'current-password' | undefined = undefined;
export let debounce: number | boolean = false;
export let debouncing = false;
export let undebouncedValue: string | undefined = undefined;
export let undebouncedValue: string | undefined | null = undefined;
export let style: string | undefined = undefined;
$: undebouncedValue = value;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/layout/AppMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<ul class="menu bg-base-100 min-w-[33%] flex-nowrap overflow-y-auto">
<header class="prose flex flex-col items-end p-4 mb-4 max-w-full">
<h2 class="mb-0">{user.name}</h2>
<span class="font-light">{user.email}</span>
<span class="font-light">{user.email ?? user.username}</span>
</header>

<li>
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/lib/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ type RegisterResponseErrors = {
type JwtTokenUser = {
sub: string
name: string
email: string
email?: string
user?: string
role: 'admin' | 'user'
proj?: string,
lock: boolean | undefined,
Expand All @@ -36,7 +37,8 @@ type JwtTokenUser = {
export type LexAuthUser = {
id: string
name: string
email: string
email?: string
username?: string
role: 'admin' | 'user'
projects: UserProjects[]
locked: boolean
Expand Down Expand Up @@ -127,12 +129,13 @@ export function getUser(cookies: Cookies): LexAuthUser | null {
}

function jwtToUser(user: JwtTokenUser): LexAuthUser {
const { sub: id, name, email, proj: projectsString, role } = user;
const { sub: id, name, email, user: username, proj: projectsString, role } = user;

return {
id,
name,
email,
username,
role,
projects: projectsStringToProjects(projectsString),
locked: user.lock === true,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/routes/(authenticated)/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@
{/if}
</svelte:fragment>

{#if !data.user.emailVerified || !$projects.length}
{#if !$projects.length}
<div class="text-lg text-secondary flex gap-4 items-center justify-center">
<span class="i-mdi-creation-outline text-xl shrink-0" />
{#if !data.user.emailVerified}
{#if !data.user.emailVerified && !data.user.createdByAdmin}
{$t('user_dashboard.not_verified')}
{:else}
{$t('user_dashboard.no_projects')}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/(authenticated)/admin/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
if (formState.name.tainted || formState.password.tainted || formState.role.tainted) {
notifySuccess($t('admin_dashboard.notifications.user_updated', { name: formState.name.currentValue }));
}
if (formState.email.changed) {
if (formState.email.changed && formState.email.currentValue) {
notifySuccess(
$t('admin_dashboard.notifications.email_need_verification', {
name: user.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
export let deleteUser: (user: User) => void;
const schema = z.object({
email: z.string().email($t('form.invalid_email')),
email: z.string().email($t('form.invalid_email')).nullish(),
name: z.string(),
password: passwordFormRules($t).or(emptyString()).default(''),
role: z.enum([UserRole.User, UserRole.Admin]),
Expand All @@ -29,12 +29,16 @@
formModal.close();
}
// This is a bit of a hack to make sure that the email field is not required if the user has no email
// even if the user edited the email field
$: if(form && $form && !$form.email && _user && !_user.email) $form.email = null;
let _user: User;
export async function openModal(user: User): Promise<FormModalResult<Schema>> {
_user = user;
userIsLocked = user.locked;
const role = user.isAdmin ? UserRole.Admin : UserRole.User;
return await formModal.open({ name: user.name, email: user.email ?? undefined, role }, async () => {
return await formModal.open({ name: user.name, email: user.email ?? null, role }, async () => {
const { error, data } = await _changeUserAccountByAdmin({
userId: user.id,
email: $form.email,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
<Markdown
md={$t('project_page.get_project.instructions_wesay', {
code: project.code,
login: encodeURIComponent(user.email),
login: encodeURIComponent(user.email ?? user.username ?? ''),
name: project.name,
})}
/>
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/routes/(authenticated)/user/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import { delay } from '$lib/util/time';
export let data: PageData;
$: user = data?.account;
$: user = data.account;
let deleteModal: DeleteUserModal;
const emailResult = useEmailResult();
Expand All @@ -35,7 +35,7 @@
}
const formSchema = z.object({
email: z.string().email($t('form.invalid_email')),
email: z.string().email($t('form.invalid_email')).nullish(),
name: z.string(),
locale: z.string().min(2),
});
Expand All @@ -55,18 +55,23 @@
return error.message;
}
if ($formState.email.changed) {
if ($formState.email.changed && $form.email) {
requestedEmail.set($form.email);
}
if ($formState.name.tainted || $formState.locale.tainted) {
notifySuccess($t('account_settings.update_success'));
}
});
// This is a bit of a hack to make sure that the email field is not required if the user has no email
// even if the user edited the email field
$: if(!$form.email && $user && !$user.email) $form.email = null;
onMount(() => {
form.set(
{
email: $user.email,
email: $user.email ?? null,
name: $user.name,
locale: $user.locale,
},
Expand Down

0 comments on commit 457a7e5

Please sign in to comment.