Skip to content

Commit

Permalink
Only show the password change section if the user has a password (#4100)
Browse files Browse the repository at this point in the history
  • Loading branch information
sandhose authored Feb 24, 2025
2 parents 1db0843 + dbcb051 commit b78b893
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 5 deletions.
10 changes: 10 additions & 0 deletions crates/handlers/src/graphql/model/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,16 @@ impl User {
)
.await
}

/// Check if the user has a password set.
async fn has_password(&self, ctx: &Context<'_>) -> Result<bool, async_graphql::Error> {
let state = ctx.state();
let mut repo = state.repository().await?;

let password = repo.user_password().active(&self.0).await?;

Ok(password.is_some())
}
}

/// A session in an application, either a compatibility or an OAuth 2.0 one
Expand Down
4 changes: 4 additions & 0 deletions frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,10 @@ type User implements Node {
"""
last: Int
): AppSessionConnection!
"""
Check if the user has a password set.
"""
hasPassword: Boolean!
}

"""
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Documents = {
"\n query UserEmailList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewer {\n __typename\n ... on User {\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n": typeof types.UserEmailListDocument,
"\n fragment UserEmailList_siteConfig on SiteConfig {\n emailChangeAllowed\n }\n": typeof types.UserEmailList_SiteConfigFragmentDoc,
"\n fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n": typeof types.BrowserSessionsOverview_UserFragmentDoc,
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n": typeof types.UserProfileDocument,
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n hasPassword\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n": typeof types.UserProfileDocument,
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n $lastActive: DateFilter\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n lastActive: $lastActive\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": typeof types.BrowserSessionListDocument,
"\n query SessionsOverview {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n": typeof types.SessionsOverviewDocument,
"\n query AppSessionsList(\n $before: String\n $after: String\n $first: Int\n $last: Int\n $lastActive: DateFilter\n ) {\n viewer {\n __typename\n\n ... on User {\n id\n appSessions(\n before: $before\n after: $after\n first: $first\n last: $last\n lastActive: $lastActive\n state: ACTIVE\n ) {\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n startCursor\n endCursor\n hasNextPage\n hasPreviousPage\n }\n }\n }\n }\n }\n": typeof types.AppSessionsListDocument,
Expand Down Expand Up @@ -91,7 +91,7 @@ const documents: Documents = {
"\n query UserEmailList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewer {\n __typename\n ... on User {\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n": types.UserEmailListDocument,
"\n fragment UserEmailList_siteConfig on SiteConfig {\n emailChangeAllowed\n }\n": types.UserEmailList_SiteConfigFragmentDoc,
"\n fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n": types.BrowserSessionsOverview_UserFragmentDoc,
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n": types.UserProfileDocument,
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n hasPassword\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n": types.UserProfileDocument,
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n $lastActive: DateFilter\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n lastActive: $lastActive\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": types.BrowserSessionListDocument,
"\n query SessionsOverview {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n": types.SessionsOverviewDocument,
"\n query AppSessionsList(\n $before: String\n $after: String\n $first: Int\n $last: Int\n $lastActive: DateFilter\n ) {\n viewer {\n __typename\n\n ... on User {\n id\n appSessions(\n before: $before\n after: $after\n first: $first\n last: $last\n lastActive: $lastActive\n state: ACTIVE\n ) {\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n startCursor\n endCursor\n hasNextPage\n hasPreviousPage\n }\n }\n }\n }\n }\n": types.AppSessionsListDocument,
Expand Down Expand Up @@ -224,7 +224,7 @@ export function graphql(source: "\n fragment BrowserSessionsOverview_user on Us
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n"): typeof import('./graphql').UserProfileDocument;
export function graphql(source: "\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n hasPassword\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n ...UserEmailList_siteConfig\n ...UserEmail_siteConfig\n ...PasswordChange_siteConfig\n }\n }\n"): typeof import('./graphql').UserProfileDocument;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,8 @@ export type User = Node & {
createdAt: Scalars['DateTime']['output'];
/** Get the list of emails, chronologically sorted */
emails: UserEmailConnection;
/** Check if the user has a password set. */
hasPassword: Scalars['Boolean']['output'];
/** ID of the object. */
id: Scalars['ID']['output'];
/** When the user was locked out. */
Expand Down Expand Up @@ -1687,7 +1689,7 @@ export type BrowserSessionsOverview_UserFragment = { __typename?: 'User', id: st
export type UserProfileQueryVariables = Exact<{ [key: string]: never; }>;


export type UserProfileQuery = { __typename?: 'Query', viewerSession: { __typename: 'Anonymous' } | { __typename: 'BrowserSession', id: string, user: { __typename?: 'User', emails: { __typename?: 'UserEmailConnection', totalCount: number } } } | { __typename: 'Oauth2Session' }, siteConfig: (
export type UserProfileQuery = { __typename?: 'Query', viewerSession: { __typename: 'Anonymous' } | { __typename: 'BrowserSession', id: string, user: { __typename?: 'User', hasPassword: boolean, emails: { __typename?: 'UserEmailConnection', totalCount: number } } } | { __typename: 'Oauth2Session' }, siteConfig: (
{ __typename?: 'SiteConfig', emailChangeAllowed: boolean, passwordLoginEnabled: boolean }
& { ' $fragmentRefs'?: { 'UserEmailList_SiteConfigFragment': UserEmailList_SiteConfigFragment;'UserEmail_SiteConfigFragment': UserEmail_SiteConfigFragment;'PasswordChange_SiteConfigFragment': PasswordChange_SiteConfigFragment } }
) };
Expand Down Expand Up @@ -2302,6 +2304,7 @@ export const UserProfileDocument = new TypedDocumentString(`
... on BrowserSession {
id
user {
hasPassword
emails(first: 0) {
totalCount
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/_account.index.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function Index(): React.ReactElement {
</>
)}

{siteConfig.passwordLoginEnabled && (
{siteConfig.passwordLoginEnabled && viewerSession.user.hasPassword && (
<>
<Collapsible.Section
defaultOpen
Expand Down
1 change: 1 addition & 0 deletions frontend/src/routes/_account.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const QUERY = graphql(/* GraphQL */ `
... on BrowserSession {
id
user {
hasPassword
emails(first: 0) {
totalCount
}
Expand Down
10 changes: 10 additions & 0 deletions frontend/stories/routes/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ const userProfileHandler = ({
passwordLoginEnabled,
passwordChangeAllowed,
emailTotalCount,
hasPassword,
}: {
emailChangeAllowed: boolean;
passwordLoginEnabled: boolean;
passwordChangeAllowed: boolean;
emailTotalCount: number;
hasPassword: boolean;
}): GraphQLHandler =>
mockUserProfileQuery(() =>
HttpResponse.json({
Expand All @@ -47,6 +49,7 @@ const userProfileHandler = ({
__typename: "BrowserSession",
id: "session-id",
user: {
hasPassword,
emails: {
totalCount: emailTotalCount,
},
Expand Down Expand Up @@ -130,6 +133,7 @@ export const MultipleEmails: Story = {
passwordChangeAllowed: true,
emailChangeAllowed: true,
emailTotalCount: 3,
hasPassword: true,
}),
threeEmailsHandler,
],
Expand All @@ -147,6 +151,7 @@ export const NoEmails: Story = {
passwordChangeAllowed: true,
emailChangeAllowed: false,
emailTotalCount: 0,
hasPassword: true,
}),
],
},
Expand All @@ -163,6 +168,7 @@ export const MultipleEmailsNoChange: Story = {
passwordChangeAllowed: true,
emailChangeAllowed: false,
emailTotalCount: 3,
hasPassword: true,
}),
threeEmailsHandler,
],
Expand All @@ -180,6 +186,7 @@ export const NoEmailChange: Story = {
passwordChangeAllowed: true,
emailChangeAllowed: false,
emailTotalCount: 1,
hasPassword: true,
}),
],
},
Expand All @@ -196,6 +203,7 @@ export const NoPasswordChange: Story = {
passwordChangeAllowed: false,
emailChangeAllowed: true,
emailTotalCount: 1,
hasPassword: true,
}),
],
},
Expand All @@ -212,6 +220,7 @@ export const NoPasswordLogin: Story = {
passwordChangeAllowed: false,
emailChangeAllowed: true,
emailTotalCount: 1,
hasPassword: true,
}),
],
},
Expand All @@ -228,6 +237,7 @@ export const NoPasswordNoEmailChange: Story = {
passwordChangeAllowed: false,
emailChangeAllowed: false,
emailTotalCount: 0,
hasPassword: false,
}),
],
},
Expand Down
1 change: 1 addition & 0 deletions frontend/tests/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const handlers = [
__typename: "BrowserSession",
id: "browser-session-id",
user: {
hasPassword: true,
emails: {
totalCount: 1,
},
Expand Down

0 comments on commit b78b893

Please sign in to comment.