From 87da966a3037a7d3406ddf4846b064c463f88fbb Mon Sep 17 00:00:00 2001
From: lcampbell2
Date: Tue, 7 Jul 2020 10:30:43 -0300
Subject: [PATCH 1/3] added summary tables to orgDetails
---
frontend/schema.faker.graphql | 8 --
frontend/src/App.js | 3 -
frontend/src/LandingPage.js | 3 +-
frontend/src/Organization.js | 10 +-
frontend/src/OrganizationDetails.js | 96 ++++++++++----
frontend/src/Organizations.js | 17 ++-
frontend/src/SummaryTable.js | 121 ++++++++++--------
.../src/__tests__/OrganizationDetails.test.js | 10 +-
frontend/src/__tests__/SummaryTable.test.js | 8 ++
frontend/src/locales/en.po | 20 +++
frontend/src/locales/fr.po | 20 +++
frontend/src/makeSummaryTableData.js | 80 +++++++++---
12 files changed, 284 insertions(+), 112 deletions(-)
diff --git a/frontend/schema.faker.graphql b/frontend/schema.faker.graphql
index 8a67f94eb..35e59a259 100755
--- a/frontend/schema.faker.graphql
+++ b/frontend/schema.faker.graphql
@@ -1276,12 +1276,6 @@ type PolicyPublished {
fo: Int @examples(values: [0])
}
-type SummaryCategory {
- name: String @examples(values: ["strong", "moderate", "weak"])
- count: Int @examples(values: [33, 33, 33])
- percentage: Int @examples(values: [33, 33, 33])
-}
-
type CategorizedSummary {
categories: [SummaryCategory]
total: Int @examples(values: [100])
@@ -1291,8 +1285,6 @@ type CategorizedSummary {
The central gathering point for all of the GraphQL queries.
"""
type Query {
- webSummary: CategorizedSummary
- emailSummary: CategorizedSummary
"""
A query object used to grab the data to create dmarc report bar graph.
"""
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 2071d8cb0..6ad96d8fa 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -64,9 +64,6 @@ export default function App() {
Sign In
)}
- {/*
- User List
- */}
Report
diff --git a/frontend/src/LandingPage.js b/frontend/src/LandingPage.js
index d1de7e1d8..389121d92 100644
--- a/frontend/src/LandingPage.js
+++ b/frontend/src/LandingPage.js
@@ -1,7 +1,7 @@
import React from 'react'
import { Trans } from '@lingui/macro'
import { Layout } from './Layout'
-import { Heading, Text, Stack } from '@chakra-ui/core'
+import { Heading, Text, Stack, Divider } from '@chakra-ui/core'
import { SummaryGroup } from './SummaryGroup'
export function LandingPage() {
@@ -27,6 +27,7 @@ export function LandingPage() {
+
*All data represented is mocked for demonstration purposes
diff --git a/frontend/src/Organization.js b/frontend/src/Organization.js
index ef4e613f1..7d0868c59 100644
--- a/frontend/src/Organization.js
+++ b/frontend/src/Organization.js
@@ -8,9 +8,13 @@ export function Organization({ name, slug, domainCount, ...rest }) {
return (
-
- {name}
-
+
+
+
+ {name}
+
+
+
Internet facing services: {domainCount}
diff --git a/frontend/src/OrganizationDetails.js b/frontend/src/OrganizationDetails.js
index 0e8cf71de..ed2912a90 100644
--- a/frontend/src/OrganizationDetails.js
+++ b/frontend/src/OrganizationDetails.js
@@ -2,13 +2,21 @@ import React from 'react'
import { useQuery } from '@apollo/react-hooks'
import { t, Trans } from '@lingui/macro'
import { Layout } from './Layout'
-import { ListOf } from './ListOf'
-import { Domain } from './Domain'
-import { Link, Icon, Heading, Stack, useToast } from '@chakra-ui/core'
+import {
+ Link,
+ Icon,
+ Heading,
+ Stack,
+ useToast,
+ Divider,
+ Text,
+} from '@chakra-ui/core'
import { ORGANIZATION_BY_SLUG } from './graphql/queries'
import { useLingui } from '@lingui/react'
import { useUserState } from './UserState'
import { Link as ReactRouterLink, useParams } from 'react-router-dom'
+import SummaryTable from './SummaryTable'
+import makeSummaryTableData from './makeSummaryTableData'
export default function OrganizationDetails() {
const { i18n } = useLingui()
@@ -34,9 +42,9 @@ export default function OrganizationDetails() {
},
})
- let domains = []
+ let domainName = ''
if (data && data.organization.domains.edges) {
- domains = data.organization.domains.edges.map((e) => e.node)
+ domainName = data.organization.name
}
if (loading) {
@@ -46,10 +54,55 @@ export default function OrganizationDetails() {
)
}
+
+ const columns = [
+ {
+ Header: i18n._(t`Domain`),
+ accessor: 'host_domain',
+ },
+ {
+ Header: 'HTTPS',
+ accessor: 'https_result',
+ },
+ {
+ Header: 'HSTS',
+ accessor: 'hsts_result',
+ },
+ {
+ Header: i18n._(t`HSTS Preloaded`),
+ accessor: 'preloaded_result',
+ },
+ {
+ Header: 'SSL',
+ accessor: 'ssl_result',
+ },
+ {
+ Header: i18n._(t`Protocols & Ciphers`),
+ accessor: 'protocol_cipher_result',
+ },
+ {
+ Header: i18n._(t`Certificate Use`),
+ accessor: 'cert_use_result',
+ },
+ {
+ Header: 'SPF',
+ accessor: 'spf_result',
+ },
+ {
+ Header: 'DKIM',
+ accessor: 'dkim_result',
+ },
+ {
+ Header: 'DMARC',
+ accessor: 'dmarc_result',
+ },
+ ]
+
+ const tableEntries = Math.floor(Math.random() * 20)
return (
-
+
- {data.organization.name}
+ {domainName}
-
-
- No domains yet.}
- >
- {({ url, lastRan }, index) => (
-
- )}
-
-
+
+ {tableEntries > 0 ? (
+
+ ) : (
+
+ No domains yet.
+
+ )}
+
+
+ *All data represented is mocked for demonstration purposes
)
}
diff --git a/frontend/src/Organizations.js b/frontend/src/Organizations.js
index 83e7c7b35..dbb8a3a2d 100644
--- a/frontend/src/Organizations.js
+++ b/frontend/src/Organizations.js
@@ -3,7 +3,7 @@ import { useQuery } from '@apollo/react-hooks'
import { Trans } from '@lingui/macro'
import { Layout } from './Layout'
import { ListOf } from './ListOf'
-import { Heading, Stack, useToast } from '@chakra-ui/core'
+import { Heading, Stack, useToast, Box, Divider } from '@chakra-ui/core'
import { ORGANIZATIONS } from './graphql/queries'
import { useUserState } from './UserState'
import { Organization } from './Organization'
@@ -55,12 +55,15 @@ export default function Organisations() {
ifEmpty={() => No Organizations}
>
{({ name, slug, domainCount }, index) => (
-
+
+
+
+
)}
diff --git a/frontend/src/SummaryTable.js b/frontend/src/SummaryTable.js
index b6fe36dd0..920364182 100644
--- a/frontend/src/SummaryTable.js
+++ b/frontend/src/SummaryTable.js
@@ -2,11 +2,10 @@ import React from 'react'
import styled from '@emotion/styled'
import { useTable, usePagination } from 'react-table'
import { array } from 'prop-types'
-import { Box, Text, Button, Stack, Select, Input } from '@chakra-ui/core'
-
+import { Trans } from '@lingui/macro'
+import { Box, Text, Stack, Select, Input, IconButton } from '@chakra-ui/core'
import WithPseudoBox from './withPseudoBox'
-// TODO: replace with values from the theme
const Table = styled.table`
& {
width: 100%;
@@ -24,6 +23,10 @@ const Table = styled.table`
text-align: center;
}
+ .pagination {
+ padding: 0.5rem;
+ }
+
.title {
text-align: center;
}
@@ -75,6 +78,10 @@ const Table = styled.table`
function SummaryTable({ ...props }) {
const { data, columns } = props
+ const defaultPageSize = window.matchMedia('screen and (max-width: 760px)')
+ .matches
+ ? 5
+ : 10
const {
getTableProps,
@@ -96,18 +103,14 @@ function SummaryTable({ ...props }) {
{
columns,
data,
- initialState: { pageIndex: 0 },
+ initialState: { pageIndex: 0, pageSize: defaultPageSize },
},
usePagination,
)
return (
-
-
-
- Domain Summary Table
-
-
+
+
{headerGroups.map((headerGroup, idx) => (
@@ -142,46 +145,64 @@ function SummaryTable({ ...props }) {
-
- {' '}
- {' '}
- {' '}
- {' '}
-
- Page {pageIndex + 1} of {pageOptions.length}{' '}
-
- | Go to page:
- {
- const page = e.target.value ? Number(e.target.value) - 1 : 0
- gotoPage(page)
- }}
- />{' '}
-
-
-
+
+
+
+ gotoPage(0)}
+ disabled={!canPreviousPage}
+ />
+ previousPage()}
+ disabled={!canPreviousPage}
+ />
+ nextPage()}
+ disabled={!canNextPage}
+ />
+ gotoPage(pageCount - 1)}
+ disabled={!canNextPage}
+ />
+
+ Page {pageIndex + 1} of{' '}
+ {pageOptions.length}{' '}
+
+
+
+
+ Go to page:
+
+ {
+ const page = e.target.value ? Number(e.target.value) - 1 : 0
+ gotoPage(page)
+ }}
+ />{' '}
+
+
+
+
+
)
}
diff --git a/frontend/src/__tests__/OrganizationDetails.test.js b/frontend/src/__tests__/OrganizationDetails.test.js
index a6a2712e0..bb1a3b898 100644
--- a/frontend/src/__tests__/OrganizationDetails.test.js
+++ b/frontend/src/__tests__/OrganizationDetails.test.js
@@ -42,6 +42,15 @@ describe('', () => {
},
},
]
+
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ })),
+ })
+
const { getByText } = render(
@@ -67,7 +76,6 @@ describe('', () => {
await waitFor(() => {
expect(getByText(name)).toBeInTheDocument()
- expect(getByText(/2020-06-18T00:42:12.414Z/)).toBeInTheDocument()
})
})
})
diff --git a/frontend/src/__tests__/SummaryTable.test.js b/frontend/src/__tests__/SummaryTable.test.js
index 8c3e10cd9..ac506f2ab 100644
--- a/frontend/src/__tests__/SummaryTable.test.js
+++ b/frontend/src/__tests__/SummaryTable.test.js
@@ -5,6 +5,14 @@ import { I18nProvider } from '@lingui/react'
import { setupI18n } from '@lingui/core'
import SummaryTable from '../SummaryTable'
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ })),
+})
+
describe('', () => {
it('renders correctly', async () => {
const columns = [{ Header: 'header', accessor: 'accessor' }]
diff --git a/frontend/src/locales/en.po b/frontend/src/locales/en.po
index 5e65b5a5b..2e3f6ebc1 100644
--- a/frontend/src/locales/en.po
+++ b/frontend/src/locales/en.po
@@ -642,3 +642,23 @@ msgstr "back to organizations"
#: src/OrganizationDetails.js:62
msgid "{0}"
msgstr "{0}"
+
+#: src/OrganizationDetails.js:60
+msgid "Domain"
+msgstr "Domain"
+
+#: src/OrganizationDetails.js:60
+msgid "HSTS Preloaded"
+msgstr "HSTS Preloaded"
+
+#: src/OrganizationDetails.js:60
+msgid "Protocols & Ciphers"
+msgstr "Protocols & Ciphers"
+
+#: src/OrganizationDetails.js:60
+msgid "Certificate Use"
+msgstr "Certificate Use"
+
+#: src/SummaryTable.js:174
+msgid "of"
+msgstr "of"
diff --git a/frontend/src/locales/fr.po b/frontend/src/locales/fr.po
index 827509d00..9b12875af 100644
--- a/frontend/src/locales/fr.po
+++ b/frontend/src/locales/fr.po
@@ -642,3 +642,23 @@ msgstr "retour aux organisations"
#: src/OrganizationDetails.js:62
msgid "{0}"
msgstr ""
+
+#: src.OrganizationDetails.js:60
+msgid "Domain"
+msgstr "Domaine"
+
+#: src.OrganizationDetails.js:60
+msgid "HSTS Preloaded"
+msgstr "HSTS Préchargé"
+
+#: src.OrganizationDetails.js:60
+msgid "Protocols & Ciphers"
+msgstr "Protocoles et Codes"
+
+#: src.OrganizationDetails.js:60
+msgid "Certificate Use"
+msgstr "Utilisation des Certificats"
+
+#: src/SummaryTable.js:174
+msgid "of"
+msgstr "de"
diff --git a/frontend/src/makeSummaryTableData.js b/frontend/src/makeSummaryTableData.js
index c8f5459e7..16b1265a6 100644
--- a/frontend/src/makeSummaryTableData.js
+++ b/frontend/src/makeSummaryTableData.js
@@ -38,22 +38,10 @@ const generateEmailStatusIcon = () => {
return statusIcon
}
-const domainNames = [
- 'cyber.gc.ca',
- 'tbs-sct.gc.ca',
- 'canada.ca',
- 'cra-arc.gc.ca',
- 'pm.gc.ca',
- 'cse-cst.gc.ca',
- 'forces.gc.ca',
- 'faker.gc.ca',
- 'rcmp-grc.gc.ca',
-]
-
-const newDomain = () => {
- const ind = Math.floor(Math.random() * 9)
+const newDomain = (names) => {
+ const ind = Math.floor(Math.random() * 13)
return {
- host_domain: domainNames[ind],
+ host_domain: names[ind],
https_result: generateWebStatusIcon(),
hsts_result: generateWebStatusIcon(),
preloaded_result: generateWebStatusIcon(),
@@ -66,12 +54,70 @@ const newDomain = () => {
}
}
-export default function makeSummaryTableData(...lens) {
+export default function MakeSummaryTableData(...lens) {
+ // const domainNames = [
+ //
+ // cyber.gc.ca
+ // ,
+ //
+ // tbs-sct.gc.ca
+ // ,
+ //
+ // canada.ca
+ // ,
+ //
+ // cra-arc.gc.ca
+ // ,
+ //
+ // pm.gc.ca
+ // ,
+ //
+ // cse-cst.gc.ca
+ // ,
+ //
+ // forces.gc.ca
+ // ,
+ //
+ // faker.gc.ca
+ // ,
+ //
+ // rcmp-grc.gc.ca
+ // ,
+ //
+ // hc-sc.gc.ca
+ // ,
+ //
+ // dfait-maeci.gc.ca
+ // ,
+ //
+ // ec.gc.ca
+ // ,
+ //
+ // dfo-mpo.gc.ca
+ // ,
+ // ]
+
+ const domainNames = [
+ 'cyber.gc.ca',
+ 'tbs-sct.gc.ca',
+ 'canada.ca',
+ 'cra-arc.gc.ca',
+ 'pm.gc.ca',
+ 'cse-cst.gc.ca',
+ 'forces.gc.ca',
+ 'faker.gc.ca',
+ 'rcmp-grc.gc.ca',
+ 'hc-sc.gc.ca',
+ 'dfait-maeci.gc.ca',
+ 'ec.gc.ca',
+ 'dfo-mpo.gc.ca',
+ ]
+
const makeDataLevel = (depth = 0) => {
const len = lens[depth]
return range(len).map(() => {
return {
- ...newDomain(),
+ ...newDomain(domainNames),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
}
})
From aa3f7441304b342015a50121e7d9c90a36cca333 Mon Sep 17 00:00:00 2001
From: lcampbell2
Date: Wed, 8 Jul 2020 10:09:36 -0300
Subject: [PATCH 2/3] updated faker
---
frontend/schema.faker.graphql | 122 ++++++++++++++++------------------
1 file changed, 58 insertions(+), 64 deletions(-)
diff --git a/frontend/schema.faker.graphql b/frontend/schema.faker.graphql
index 35e59a259..1cd3afc6e 100755
--- a/frontend/schema.faker.graphql
+++ b/frontend/schema.faker.graphql
@@ -411,27 +411,6 @@ type DmarcReportDetailTables {
detailTables: DetailTables
}
-"""
-Input object containing fields which map to the required arguments for
-dmarcReportDetailTablesInput
-"""
-input DmarcReportDetailTablesInput {
- """
- The slugified version of the domain you wish to retrieve data for.
- """
- domainSlug: Slug!
-
- """
- The period in which the returned data is relevant to.
- """
- period: PeriodEnums!
-
- """
- The year in which the returned data is relevant to.
- """
- year: Year!
-}
-
"""
A query object used to grab the data to create dmarc report doughnuts
"""
@@ -452,21 +431,6 @@ type DmarcReportSummary {
categoryTotals: CategoryTotals
}
-"""
-This object is used to define the various arguments used in the dmarc report
-summary query
-"""
-input DmarcReportSummaryInput {
- """The slugified version of the domain you wish to retrieve data for."""
- domainSlug: Slug!
-
- """The period in which the returned data is relevant to."""
- period: PeriodEnums!
-
- """The year in which the returned data is relevant to."""
- year: Year!
-}
-
"""
A query object used to grab the data to create dmarc report bar graph
"""
@@ -487,15 +451,6 @@ type DmarcReportSummaryList {
categoryTotals: CategoryTotals
}
-"""
-This object is used to define the various arguments used in the dmarc report
-summary list query
-"""
-input DmarcReportSummaryListInput {
- """The slugified version of the domain you wish to retrieve data for."""
- domainSlug: Slug!
-}
-
type Domain implements Node {
organization: Organizations
@@ -1276,11 +1231,6 @@ type PolicyPublished {
fo: Int @examples(values: [0])
}
-type CategorizedSummary {
- categories: [SummaryCategory]
- total: Int @examples(values: [100])
-}
-
"""
The central gathering point for all of the GraphQL queries.
"""
@@ -1290,9 +1240,9 @@ type Query {
"""
dmarcReportSummaryList(
"""
- Input argument with various input fields required for the dmarc report summary list query
+ The slugified version of the domain you wish to retrieve data for.
"""
- input: DmarcReportSummaryListInput!
+ domainSlug: Slug!
): [DmarcReportSummaryList] @listLength(min: 13, max: 13)
"""
@@ -1300,9 +1250,9 @@ type Query {
"""
demoDmarcReportSummaryList(
"""
- Input argument with various input fields required for the dmarc report summary list query
+ The slugified version of the domain you wish to retrieve data for.
"""
- input: DmarcReportSummaryListInput!
+ domainSlug: Slug!
): [DmarcReportSummaryList] @listLength(min: 13, max: 13)
"""
@@ -1310,9 +1260,19 @@ type Query {
"""
dmarcReportDetailTables(
"""
- Input object containing fields which map to the required arguments for dmarcReportDetailTablesInput
+ The slugified version of the domain you wish to retrieve data for.
+ """
+ domainSlug: Slug!
+
"""
- input: DmarcReportDetailTablesInput!
+ The period in which the returned data is relevant to.
+ """
+ period: PeriodEnums!
+
+ """
+ The year in which the returned data is relevant to.
+ """
+ year: Year!
): DmarcReportDetailTables
"""
@@ -1320,25 +1280,59 @@ type Query {
"""
demoDmarcReportDetailTables(
"""
- Input object containing fields which map to the required arguments for dmarcReportDetailTablesInput
+ The slugified version of the domain you wish to retrieve data for.
+ """
+ domainSlug: Slug!
+
+ """
+ The period in which the returned data is relevant to.
"""
- input: DmarcReportDetailTablesInput!
+ period: PeriodEnums!
+
+ """
+ The year in which the returned data is relevant to.
+ """
+ year: Year!
): DmarcReportDetailTables
- """A query object used to grab the data to create dmarc report doughnuts"""
+ """
+ A query object used to grab the data to create dmarc report doughnuts
+ """
dmarcReportSummary(
"""
- Input argument with various input fields required for the dmarc report summary query
+ The slugified version of the domain you wish to retrieve data for.
+ """
+ domainSlug: Slug!
+
+ """
+ The period in which the returned data is relevant to.
+ """
+ period: PeriodEnums!
+
"""
- input: DmarcReportSummaryInput!
+ The year in which the returned data is relevant to.
+ """
+ year: Year!
): DmarcReportSummary
- """A query object used to grab the data to create dmarc report doughnuts"""
+ """
+ A query object used to grab the data to create dmarc report doughnuts
+ """
demoDmarcReportSummary(
"""
- Input argument with various input fields required for the dmarc report summary query
+ The slugified version of the domain you wish to retrieve data for.
+ """
+ domainSlug: Slug!
+
+ """
+ The period in which the returned data is relevant to.
+ """
+ period: PeriodEnums!
+
+ """
+ The year in which the returned data is relevant to.
"""
- input: DmarcReportSummaryInput!
+ year: Year!
): DmarcReportSummary
"""
From aa00b8119d005588d06838cf0cd5942884987c82 Mon Sep 17 00:00:00 2001
From: lcampbell2
Date: Thu, 9 Jul 2020 08:12:59 -0300
Subject: [PATCH 3/3] updated faker
---
frontend/schema.faker.graphql | 65 +++++++++++++++++++++++++++++++----
1 file changed, 59 insertions(+), 6 deletions(-)
diff --git a/frontend/schema.faker.graphql b/frontend/schema.faker.graphql
index 1cd3afc6e..02a9070b6 100755
--- a/frontend/schema.faker.graphql
+++ b/frontend/schema.faker.graphql
@@ -729,11 +729,6 @@ enum LanguageEnums {
The central gathering point for all of the GraphQL mutations.
"""
type Mutation {
- updatePassword(
- confirmPassword: String!
- password: String!
- userName: EmailAddress!
- ): UpdateUserPassword
authenticateTwoFactor(
otpCode: String!
userName: EmailAddress!
@@ -873,6 +868,26 @@ type Mutation {
"""
userName: EmailAddress!
): SendEmailVerification
+
+ """
+ Allows users to update their password, using a token sent to them via email.
+ """
+ updatePassword(
+ """
+ An input object containing all fields for this mutation.
+ """
+ input: UpdateUserPasswordInput!
+ ): UpdateUserPassword
+
+ """
+ Allows the user to request an email to reset their account password.
+ """
+ sendPasswordResetLink(
+ """
+ User name for the account you would like to receive a password reset link for.
+ """
+ userName: EmailAddress!
+ ): SendPasswordResetLink
}
"""
@@ -1560,6 +1575,17 @@ type SendEmailVerification {
status: Boolean
}
+"""
+This mutation allows a user to provide their username and request that a
+password reset email be sent to their account with a reset token in a url.
+"""
+type SendPasswordResetLink {
+ """
+ Status of a successful password reset email.
+ """
+ status: String
+}
+
"""
This method allows for new users to sign up for our sites services.
"""
@@ -1828,8 +1854,35 @@ input UpdateOrganizationInput {
city: String
}
+"""
+This mutation allows users to use a token sent to them by email to update
+their account password.
+"""
type UpdateUserPassword {
- user: User
+ """
+ Informs the user if the password update was successful.
+ """
+ status: String
+}
+
+"""
+Input object containing all fields for the UpdateUserPassword mutation
+"""
+input UpdateUserPasswordInput {
+ """
+ Reset token found in the url, which was sent to the users inbox, by the sendPasswordReset mutation.
+ """
+ resetToken: String!
+
+ """
+ The new password the user wishes to change to.
+ """
+ password: String!
+
+ """
+ A confirmation of the password field to ensure the user has entered the new password correctly.
+ """
+ confirmPassword: String!
}
type UpdateUserRole {