task(admin): Show if password is set on account

Because:
- We need to know if an accounts password was set

This Commit:
- Adds a row for Password Set
- Exposes the verifierSetAt field to the front end
- Uses verifierSetAt to determine if a password was set
- Fixes bug where dev mode wasn't starting due wrong path
- Fixes bug where StrictMode was stopping lazy queries from executing
This commit is contained in:
dschom 2023-08-28 09:16:57 -07:00
Родитель 295130c0b3
Коммит 4ed12f08ef
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: F26AEE99174EE68B
12 изменённых файлов: 71 добавлений и 11 удалений

Просмотреть файл

@ -51,6 +51,7 @@ let accountResponse: AccountProps = {
createdAt: 1589467100316,
disabledAt: null,
lockedAt: null,
verifierSetAt: 1589467100316,
emailBounces: [
{
bounceSubType: BounceSubType.NoEmail,
@ -146,6 +147,34 @@ it('displays when account is locked', async () => {
);
});
it('displays when account password is not set', async () => {
const lockedAccount = {
...accountResponse,
verifierSetAt: 0,
};
const { getByTestId } = render(
<MockedProvider>
<Account {...lockedAccount} />
</MockedProvider>
);
expect(getByTestId('account-password-set')).toHaveTextContent('No');
});
it('displays when account password is set', async () => {
const lockedAccount = {
...accountResponse,
verifierSetAt: 1589467100316,
};
const { getByTestId } = render(
<MockedProvider>
<Account {...lockedAccount} />
</MockedProvider>
);
expect(getByTestId('account-password-set')).toHaveTextContent('Yes');
});
it('displays the unconfirmed account', async () => {
accountResponse.emails![0].isVerified = false;
const { getByTestId } = render(

Просмотреть файл

@ -90,6 +90,7 @@ export const Account = ({
securityEvents,
linkedAccounts,
accountEvents,
verifierSetAt,
}: AccountProps) => {
const createdAtDate = getFormattedDate(createdAt);
const disabledAtDate = getFormattedDate(disabledAt);
@ -146,6 +147,11 @@ export const Account = ({
children={`${createdAtDate} (${createdAt})`}
testId="account-created-at"
/>
<TableRowYHeader
header="Password Set"
children={verifierSetAt != null && verifierSetAt > 0 ? 'Yes' : 'No'}
testId="account-password-set"
/>
<TableRowYHeader
header="Locale"
children={
@ -358,6 +364,7 @@ export const Account = ({
<TableXHeaders rowHeaders={['Event', 'Timestamp', 'Action']}>
{linkedAccounts.map((linkedAccount: LinkedAccountType) => (
<LinkedAccount
key={linkedAccount.uid}
{...{
uid,
providerId: linkedAccount.providerId,

Просмотреть файл

@ -10,6 +10,7 @@ export const GET_ACCOUNT_BY_EMAIL = gql`
uid
createdAt
disabledAt
verifierSetAt
lockedAt
locale
email
@ -100,6 +101,7 @@ export const GET_ACCOUNT_BY_UID = gql`
uid
createdAt
disabledAt
verifierSetAt
lockedAt
locale
email

Просмотреть файл

@ -89,6 +89,7 @@ class GetAccountsByEmail {
accountByEmail: {
uid: '123',
createdAt: 1658534643990,
verifierSetAt: 1589467100316,
disabledAt: null,
locale: testLocale,
lockedAt: null,
@ -128,6 +129,7 @@ class GetAccountsByEmail {
data: {
accountByEmail: {
uid: 'a1b2c3',
verifierSetAt: 1589467100316,
email: email,
emails: [
{

Просмотреть файл

@ -44,15 +44,17 @@ try {
const root = createRoot(document.getElementById('root')!);
root.render(
<React.StrictMode>
<AppErrorBoundary>
<ApolloProvider {...{ client }}>
<AppLocalizationProvider>
<App {...{ config }} />
</AppLocalizationProvider>
</ApolloProvider>
</AppErrorBoundary>
</React.StrictMode>
// TODO - Add StrictMode - We need to figure out
// why strict mode now appears to be breaking queries.
// <React.StrictMode>
<AppErrorBoundary>
<ApolloProvider {...{ client }}>
<AppLocalizationProvider>
<App {...{ config }} />
</AppLocalizationProvider>
</ApolloProvider>
</AppErrorBoundary>
// </React.StrictMode>
);
} catch (error) {
console.error('Error initializing fxa-admin-panel', error);

Просмотреть файл

@ -6,7 +6,7 @@ This is the GraphQL server for an internal resource for FxA Admins to access a s
The [GraphQL playground](https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/) for this package is available at [localhost:8095/graphql](http://localhost:8095/graphql), providing a GUI for an up-to-date schema and API docs, as well as a way to test queries and mutations.
The playground requires an `oidc-claim-id-token-email` authorization header. In production this is supplied through an nginx header after LDAP credentials, which have been verified but in development, a dummy email should be supplied in the bottom left-hand corner of the GQL playground labeled "HTTP Headers":
The playground requires an `oidc-claim-id-token-email` authorization header. In production this is supplied through an nginx header after LDAP credentials have been verified, but in development, a dummy email should be supplied in the bottom left-hand corner of the GQL playground labeled "HTTP Headers":
In addition a `remote-groups` header must also be set to indicate the user's LDAP group membership. Again, in production this will be set by nginx, but in development, a dummy value must be supplied.

Просмотреть файл

@ -15,5 +15,5 @@
}
]
},
"entryFile": "src/packages/admin-server/main.js"
"entryFile": "packages/fxa-admin-server/src/main.js"
}

Просмотреть файл

@ -233,6 +233,7 @@ describe('#integration - AccountResolver', () => {
const result = (await resolver.accountByUid(USER_1.uid, 'joe')) as Account;
expect(result).toBeDefined();
expect(result.email).toBe(USER_1.email);
expect(result.verifierSetAt).toBeGreaterThan(0);
expect(logger.info).toBeCalledTimes(2);
});
@ -465,4 +466,15 @@ describe('#integration - AccountResolver', () => {
expect(result).toBeDefined();
expect(result.length).toBe(1);
});
it('loads verifierSetAt', async () => {
const user = (await resolver.accountByEmail(
USER_1.email,
true,
'joe'
)) as Account;
expect(user).toBeDefined();
expect(user.verifierSetAt).toBeDefined();
});
});

Просмотреть файл

@ -50,6 +50,7 @@ const ACCOUNT_COLUMNS = [
'createdAt',
'disabledAt',
'lockedAt',
'verifierSetAt',
'locale',
];
const EMAIL_COLUMNS = [

Просмотреть файл

@ -36,6 +36,9 @@ export class Account {
@Field({ nullable: true })
public lockedAt?: number;
@Field({ nullable: true })
public verifierSetAt?: number;
@Field((type) => [Email], { nullable: true })
public emails!: Email[];

Просмотреть файл

@ -145,6 +145,7 @@ export interface Account {
disabledAt?: Nullable<number>;
locale?: Nullable<string>;
lockedAt?: Nullable<number>;
verifierSetAt?: Nullable<number>;
emails?: Nullable<Email[]>;
emailBounces?: Nullable<EmailBounce[]>;
totp?: Nullable<Totp[]>;

Просмотреть файл

@ -140,6 +140,7 @@ type Account {
disabledAt: Float
locale: String
lockedAt: Float
verifierSetAt: Float
emails: [Email!]
emailBounces: [EmailBounce!]
totp: [Totp!]