From bebe3b9c5e03ca0b67491fde02c5c751b1aa46e5 Mon Sep 17 00:00:00 2001 From: Aimery Thomas Date: Thu, 4 Nov 2021 20:25:41 +0100 Subject: [PATCH] Add grouping options and sample data --- .../src/models/IApplication.ts | 5 +- .../src/models/SampleApplications.json | 175 ++++++++++++++++++ .../GraphAppSecretExpirationWebPart.ts | 46 ++++- .../GraphAppSecretExpiration.module.scss | 3 +- .../components/GraphAppSecretExpiration.tsx | 48 +++-- .../IGraphAppSecretExpirationProps.ts | 4 +- .../graphAppSecretExpiration/loc/en-us.js | 12 +- .../loc/mystrings.d.ts | 4 + .../tsconfig.json | 2 + 9 files changed, 270 insertions(+), 29 deletions(-) create mode 100644 samples/react-graph-app-secret-expiration/src/models/SampleApplications.json diff --git a/samples/react-graph-app-secret-expiration/src/models/IApplication.ts b/samples/react-graph-app-secret-expiration/src/models/IApplication.ts index ca0f8713c..a127e4ed1 100644 --- a/samples/react-graph-app-secret-expiration/src/models/IApplication.ts +++ b/samples/react-graph-app-secret-expiration/src/models/IApplication.ts @@ -12,7 +12,7 @@ export interface IApplication { export interface IPasswordCredential { customKeyIdentifier: string; displayName: string; - endDateTime: Date; + endDateTime: string; hint: string; keyId: string; secretText: string; @@ -22,7 +22,7 @@ export interface IPasswordCredential { export interface IKeyCredential { customKeyIdentifier: string; displayName: string; - endDateTime: Date; + endDateTime: string; key: string; keyId: string; startDateTime: string; @@ -37,4 +37,5 @@ export interface IFormattedApplication { daysLeft: number; type: string; expirationDate: string; + linkTitle: string; } \ No newline at end of file diff --git a/samples/react-graph-app-secret-expiration/src/models/SampleApplications.json b/samples/react-graph-app-secret-expiration/src/models/SampleApplications.json new file mode 100644 index 000000000..a941f1877 --- /dev/null +++ b/samples/react-graph-app-secret-expiration/src/models/SampleApplications.json @@ -0,0 +1,175 @@ +{ + "applications": [ + { + "appId": "bb4bcecc-d0cb-478a-9d48-1527cb7b65c0", + "displayName": "API Backend", + "keyCredentials": [], + "passwordCredentials": [ + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2020-09-20T15:47:46.5876682Z", + "hint": "42", + "keyId": "c2435363-4f50-4c14-8994-0122e77d84c9", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Postman", + "endDateTime": "2021-11-25T16:25:17.375Z", + "hint": "42", + "keyId": "7ead14df-56e8-4a24-9f6e-6692ae17e84f", + "secretText": null, + "startDateTime": "2021-09-20T15:25:17.375Z" + }, + { + "customKeyIdentifier": null, + "displayName": "PnP", + "endDateTime": "2022-02-04T06:58:04.525Z", + "hint": "42", + "keyId": "8fc28fb8-ab39-4623-bd75-6d7baf5f205d", + "secretText": null, + "startDateTime": "2021-02-04T06:58:17.678Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Dev", + "endDateTime": "2021-12-15T16:36:58.443Z", + "hint": "42", + "keyId": "ef77eda1-741c-4b51-9de7-aa455831dff0", + "secretText": null, + "startDateTime": "2020-12-15T16:37:05.242Z" + } + ] + }, + { + "appId": "8537b74d-1bfe-41ba-849a-eb25917d3746", + "displayName": "Client API", + "keyCredentials": [], + "passwordCredentials": [ + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2022-09-20T15:47:46.5876682Z", + "hint": "42", + "keyId": "7e6dd1c3-2366-4c5a-9a3f-d1c83f40ba11", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2021-12-05T15:47:46.5876682Z", + "hint": "42", + "keyId": "7c341ab9-089e-47e1-946b-60b155c3e94c", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2021-12-01T15:47:46.5876682Z", + "hint": "42", + "keyId": "24d724d8-71b3-471e-9b92-a5bd7e9bb6d6", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2021-11-20T15:47:46.5876682Z", + "hint": "42", + "keyId": "4488b5fa-01a0-4191-a96e-de3a83cae203", + "secretText": null, + "startDateTime": "2021-11-20T15:47:46.5876682Z" + } + ] + }, + { + "appId": "e2bfaff8-9a03-4837-886e-b110b4972a47", + "displayName": "Graph Console App", + "keyCredentials": [], + "passwordCredentials": [ + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2021-12-28T15:47:46.5876682Z", + "hint": "42", + "keyId": "7791e9be-defd-4588-9612-ea6404af1875", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2022-01-03T15:47:46.5876682Z", + "hint": "42", + "keyId": "8962ac05-6a3e-4f9a-b91f-e0212cd9847b", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2022-11-18T15:47:46.5876682Z", + "hint": "42", + "keyId": "85bc5551-1165-4e04-aa77-0d0c8f32ab1f", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2020-12-28T15:47:46.5876682Z", + "hint": "42", + "keyId": "cd909eca-bc21-4005-b346-7e2ef47fe26b", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + } + ] + }, + { + "appId": "b166b3ca-38e0-4dd5-80e4-03f51509a8eb", + "displayName": "PnP Modernization Scanner", + "keyCredentials": [ + { + "customKeyIdentifier": "SampleId", + "displayName": "PnP Modernization Scanner", + "endDateTime": "2031-01-11T23:00:00Z", + "key": "SampleKey", + "keyId": "9942dd18-a265-422f-bbfe-b8fdd866c3d3", + "startDateTime": "2021-01-11T23:00:00Z", + "type": "AsymmetricX509Cert", + "usage": "Verify" + } + ], + "passwordCredentials": [] + }, + { + "appId": "14ce9f15-41ed-446b-8733-a0eeb1379b38", + "displayName": "Bot - OfficeDev", + "keyCredentials": [], + "passwordCredentials": [ + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2023-09-20T15:47:46.5876682Z", + "hint": "42", + "keyId": "cb8cf8ec-630b-4da6-a578-4d27742d069c", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + }, + { + "customKeyIdentifier": null, + "displayName": "Password friendly name", + "endDateTime": "2024-09-20T15:47:46.5876682Z", + "hint": "42", + "keyId": "2bfdc738-db27-4b3d-898d-ce3f57ab81eb", + "secretText": null, + "startDateTime": "2021-09-20T15:47:46.5876682Z" + } + ] + } + ] +} \ No newline at end of file diff --git a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/GraphAppSecretExpirationWebPart.ts b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/GraphAppSecretExpirationWebPart.ts index 76f597cad..2f331b347 100644 --- a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/GraphAppSecretExpirationWebPart.ts +++ b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/GraphAppSecretExpirationWebPart.ts @@ -3,7 +3,9 @@ import * as ReactDom from 'react-dom'; import { Version } from '@microsoft/sp-core-library'; import { IPropertyPaneConfiguration, - PropertyPaneTextField + IPropertyPaneDropdownOption, + PropertyPaneDropdown, + PropertyPaneToggle } from '@microsoft/sp-property-pane'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; @@ -13,12 +15,19 @@ import { IGraphAppSecretExpirationProps } from './components/IGraphAppSecretExpi import { MSGraphClient } from '@microsoft/sp-http'; export interface IGraphAppSecretExpirationWebPartProps { - description: string; + groupByColumn: string; + expiringSoon: boolean; + displaySampleData: boolean; + } export default class GraphAppSecretExpirationWebPart extends BaseClientSideWebPart { private graphClient: MSGraphClient; - + private dropdownOptions: IPropertyPaneDropdownOption[] = [ + {key:"none",text:"None"}, + {key:"applicationId",text:"Application ID"}, + {key:"type",text:"Type"}]; + public onInit(): Promise { return new Promise((resolve: () => void, reject: (error: any) => void): void => { this.context.msGraphClientFactory @@ -34,8 +43,10 @@ export default class GraphAppSecretExpirationWebPart extends BaseClientSideWebPa const element: React.ReactElement = React.createElement( GraphAppSecretExpiration, { - description: this.properties.description, - graphClient: this.graphClient + graphClient: this.graphClient, + groupByColumn: this.properties.groupByColumn, + expiringSoon: this.properties.expiringSoon, + displaySampleData: this.properties.displaySampleData } ); @@ -61,9 +72,28 @@ export default class GraphAppSecretExpirationWebPart extends BaseClientSideWebPa { groupName: strings.BasicGroupName, groupFields: [ - PropertyPaneTextField('description', { - label: strings.DescriptionFieldLabel - }) + PropertyPaneDropdown('groupByColumn', { + label: strings.DefaultGroupColumnFieldLabel, + options: this.dropdownOptions, + selectedKey: "none" + }), + PropertyPaneToggle('expiringSoon', { + label: strings.DisplayOnlySecretsFieldLabel, + onText:"Yes", + offText:"No" + + }) + ] + }, + { + groupName: strings.OtherGroupName, + groupFields: [ + PropertyPaneToggle('displaySampleData', { + label: strings.DisplaySampleDataFieldLabel, + onText:"Yes", + offText:"No" + + }) ] } ] diff --git a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.module.scss b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.module.scss index f67e029de..d0b53f274 100644 --- a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.module.scss +++ b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.module.scss @@ -4,7 +4,8 @@ .container { max-width: 900px; margin: 0px auto; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + border: 1px solid lightgray + //box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); } .row { diff --git a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.tsx b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.tsx index 838e5e4e1..3cd993dd5 100644 --- a/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.tsx +++ b/samples/react-graph-app-secret-expiration/src/webparts/graphAppSecretExpiration/components/GraphAppSecretExpiration.tsx @@ -5,9 +5,10 @@ import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp import { IApplications, IApplication, IFormattedApplication } from '../../../models/IApplication'; import { IGraphAppSecretExpirationState } from './GraphAppSecretExpirationState'; import * as moment from 'moment'; -import { DefaultButton, Spinner, mergeStyles, SearchBox } from '@fluentui/react'; +import { Spinner, mergeStyles, SearchBox } from '@fluentui/react'; import { Pagination } from "@pnp/spfx-controls-react/lib/pagination"; import * as strings from 'GraphAppSecretExpirationWebPartStrings'; +import sampleApplications from '../../../models/SampleApplications.json'; const stackItemHidden = mergeStyles({ display: 'none', @@ -16,7 +17,8 @@ const _viewFields: IViewField[] = [ { name: "applicationId", displayName: "Application ID", - minWidth: 250 + minWidth: 250, + linkPropertyName: "linkTitle" }, { name: "displayName", @@ -71,7 +73,12 @@ export default class GraphAppSecretExpiration extends React.Component 0 ? daysBeforeExpiration : 0 + daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0, + linkTitle: "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Credentials/appId/"+app.appId }; - displayedApplication.push(formatedApp); + if (this.props.expiringSoon){ + if(daysBeforeExpiration < 30){ + displayedApplication.push(formatedApp); + } + } else { + displayedApplication.push(formatedApp); + } }); app.keyCredentials.forEach(keyCred => { let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays(); @@ -137,18 +151,27 @@ export default class GraphAppSecretExpiration extends React.Component 0 ? daysBeforeExpiration : 0 + daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0, + linkTitle: "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Credentials/appId/"+app.appId }; - displayedApplication.push(formatedApp); + if (this.props.expiringSoon){ + if(daysBeforeExpiration < 30){ + displayedApplication.push(formatedApp); + } + } else { + displayedApplication.push(formatedApp); + } }); }); this.setState({ applications: displayedApplication, - filteredApplications: displayedApplication + filteredApplications: displayedApplication, + loading: false }); } catch (error) { console.log(error); } + this._groupView(); } private _getPage(selectedPage: number) { @@ -194,10 +217,10 @@ export default class GraphAppSecretExpiration extends React.Component { - if (this.state.groupByFields.length === 0) { + if (this.props.groupByColumn !== "none") { let groupByFields: IGrouping[] = [ { - name: "applicationId", + name: this.props.groupByColumn, order: GroupOrder.ascending } ]; @@ -218,14 +241,13 @@ export default class GraphAppSecretExpiration extends React.Component

-

Application list :

+

Certificates and secrets :


- - this._filterApplication(text, false)} onClear={() => this._filterApplication("", true)} value={this.state.filterValue} /> + this._filterApplication(text, true)} onClear={() => this._filterApplication("", false)} value={this.state.filterValue} />