зеркало из https://github.com/mozilla/fxa.git
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:
Родитель
295130c0b3
Коммит
4ed12f08ef
|
@ -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!]
|
||||
|
|
Загрузка…
Ссылка в новой задаче