Skip to content

Commit

Permalink
Chore: Release 8.1.0 (#2505)
Browse files Browse the repository at this point in the history
Chore: Release 8.1.0
  • Loading branch information
thewahome authored Apr 11, 2023
2 parents c1282bd + 557db19 commit b6dca39
Show file tree
Hide file tree
Showing 273 changed files with 1,094 additions and 336,137 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ yarn-error.log*
.idea
.vs
*.xml
playwright-report/index.html
playwright-report
test-results

7 changes: 0 additions & 7 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ jobs:
steps:
- task: CredScan@3

- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: "Register"
verbosity: "Verbose"
alertWarningLevel: "High"
ignoreDirectories: ".github,.vscode/"

- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: "Register"
Expand Down
39 changes: 25 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "graph-explorer-v2",
"version": "8.0.0",
"version": "8.1.0",
"private": true,
"dependencies": {
"@augloop/types-core": "file:packages/types-core-2.16.189.tgz",
Expand Down Expand Up @@ -60,7 +60,7 @@
"typescript": "4.9.5",
"url": "0.11.0",
"url-loader": "4.1.1",
"webpack": "5.75.0",
"webpack": "5.76.0",
"webpack-dev-server": "4.11.1",
"webpack-manifest-plugin": "5.0.0",
"workbox-webpack-plugin": "6.5.4"
Expand All @@ -78,7 +78,7 @@
"test-playwright": "start-server-and-test start-server http://localhost:3000 test-ui",
"test-ui": "npx playwright test src/tests/ui/",
"test-playwright-accessibility": "start-server-and-test start-server http://localhost:3000 test-accessibility",
"test-accessibility": "npx playwright test src/tests/accessibility"
"test-accessibility": "npx playwright test src/tests/accessibility --project=Ms-Edge"
},
"eslintConfig": {
"extends": "react-app"
Expand Down Expand Up @@ -136,4 +136,4 @@
"resolutions": {
"@types/react": "17.0.44"
}
}
}
18 changes: 16 additions & 2 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require('dotenv').config();
import type { PlaywrightTestConfig } from '@playwright/test';
import { PlaywrightTestConfig } from '@playwright/test';

const baseURL = process.env.PLAYWRIGHT_TESTS_BASE_URL!;

Expand All @@ -26,6 +26,20 @@ const config: PlaywrightTestConfig = {
]
],
retries: 1,
timeout: 60000
timeout: 60000,
projects: [
{
name: 'Ms-Edge',
use: {
channel: 'msedge',
viewport: { width: 1920, height: 1080 }}
},
{
name: 'Chrome',
use: {
channel: 'chrome',
viewport: { width: 1365, height: 768 }}
}
]
};
export default config;
100 changes: 87 additions & 13 deletions src/app/services/actions/permissions-action-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
FETCH_FULL_SCOPES_SUCCESS,
FETCH_URL_SCOPES_SUCCESS,
GET_ALL_PRINCIPAL_GRANTS_SUCCESS, GET_ALL_PRINCIPAL_GRANTS_ERROR, REVOKE_SCOPES_PENDING,
REVOKE_SCOPES_SUCCESS, REVOKE_SCOPES_ERROR
REVOKE_SCOPES_SUCCESS, REVOKE_SCOPES_ERROR, GET_ALL_PRINCIPAL_GRANTS_PENDING
} from '../redux-constants';
import {
getAuthTokenSuccess,
Expand All @@ -32,6 +32,7 @@ import { setQueryResponseStatus } from './query-status-action-creator';
import { RevokePermissionsUtil, REVOKE_STATUS } from './permissions-action-creator.util';
import { componentNames, eventTypes, telemetry } from '../../../telemetry';
import { RevokeScopesError } from '../../utils/error-utils/RevokeScopesError';
import { IOAuthGrantPayload, IPermissionGrant } from '../../../types/permissions';

export function fetchFullScopesSuccess(response: object): AppAction {
return {
Expand Down Expand Up @@ -68,6 +69,13 @@ export function fetchScopesError(response: object): AppAction {
};
}

export function getAllPrincipalGrantsPending(response: boolean){
return {
type: GET_ALL_PRINCIPAL_GRANTS_PENDING,
response
};
}

export function getAllPrincipalGrantsSuccess(response: object): AppAction {
return {
type: GET_ALL_PRINCIPAL_GRANTS_SUCCESS,
Expand Down Expand Up @@ -143,7 +151,6 @@ export function fetchScopes() {
const response = await fetch(permissionsUrl, options);
if (response.ok) {
const scopes = await response.json();

return permissionsPanelOpen ? dispatch(fetchFullScopesSuccess({
scopes: { fullPermissions: scopes }
})) :
Expand Down Expand Up @@ -173,11 +180,12 @@ export function getPermissionsScopeType(profile: IUser | null | undefined) {
export function consentToScopes(scopes: string[]) {
return async (dispatch: Function, getState: Function) => {
try {
const { profile }: ApplicationState = getState();
const { profile, consentedScopes }: ApplicationState = getState();
const authResponse = await authenticationWrapper.consentToScopes(scopes);
if (authResponse && authResponse.accessToken) {
dispatch(getAuthTokenSuccess(true));
dispatch(getConsentedScopesSuccess(authResponse.scopes));
const validatedScopes = validateConsentedScopes(scopes, consentedScopes, authResponse.scopes);
dispatch(getConsentedScopesSuccess(validatedScopes));
if (
authResponse.account &&
authResponse.account.localAccountId !== profile?.id
Expand All @@ -191,6 +199,7 @@ export function consentToScopes(scopes: string[]) {
ok: true,
messageType: MessageBarType.success
}))
dispatch(fetchAllPrincipalGrants());
}
} catch (error: any) {
const { errorCode } = error;
Expand All @@ -207,6 +216,18 @@ export function consentToScopes(scopes: string[]) {
};
}

const validateConsentedScopes = (scopeToBeConsented: string[], consentedScopes: string[],
consentedResponse: string[]) => {
if(!consentedScopes || !consentedResponse || !scopeToBeConsented) {
return consentedResponse;
}
const expectedScopes = [...consentedScopes, ...scopeToBeConsented];
if (expectedScopes.length === consentedResponse.length) {
return consentedResponse;
}
return expectedScopes;
}

export function revokeScopes(permissionToRevoke: string) {
return async (dispatch: Function, getState: Function) => {
const { consentedScopes, profile } = getState();
Expand All @@ -230,7 +251,8 @@ export function revokeScopes(permissionToRevoke: string) {

let updatedScopes;
if (permissionBeingRevokedIsAllPrincipal && userIsTenantAdmin) {
updatedScopes = await revokePermissionUtil.updateAllPrincipalPermissionGrant(grantsPayload, permissionToRevoke);
updatedScopes = await revokePermissionUtil.
getUpdatedAllPrincipalPermissionGrant(grantsPayload, permissionToRevoke);
}
else {
updatedScopes = await revokePermissionUtil.
Expand Down Expand Up @@ -292,18 +314,70 @@ const trackRevokeConsentEvent = (status: string, permissionObject: any) => {
export function fetchAllPrincipalGrants() {
return async (dispatch: Function, getState: Function) => {
try {
const { profile } = getState();
const revokePermissionUtil = await RevokePermissionsUtil.initialize(profile.id);
const { profile, consentedScopes, scopes } = getState();
let tenantWideGrant: IOAuthGrantPayload = scopes.data.tenantWidePermissionsGrant;
let revokePermissionUtil = await RevokePermissionsUtil.initialize(profile.id);
const servicePrincipalAppId = revokePermissionUtil.getServicePrincipalAppId();
dispatch(getAllPrincipalGrantsPending(true));
let requestCounter = 0;

if (servicePrincipalAppId) {
const tenantWideGrant = revokePermissionUtil.getGrantsPayload();
dispatch(getAllPrincipalGrantsSuccess(tenantWideGrant.value));
}
else {
dispatch(getAllPrincipalGrantsError({}));
tenantWideGrant = revokePermissionUtil.getGrantsPayload();
if(tenantWideGrant){
if (!allScopesHaveConsentType(consentedScopes, tenantWideGrant, profile.id)){
while (requestCounter < 10 && profile && profile.id &&
!allScopesHaveConsentType(consentedScopes, tenantWideGrant, profile.id)) {
requestCounter += 1;
revokePermissionUtil = await RevokePermissionsUtil.initialize(profile.id);
dispatch(getAllPrincipalGrantsPending(true));
tenantWideGrant = revokePermissionUtil.getGrantsPayload();
}
dispatchGrantsStatus(dispatch, tenantWideGrant.value)
}
else{
dispatchGrantsStatus(dispatch, tenantWideGrant.value)
}
}
}
} catch (error: any) {
dispatch(getAllPrincipalGrantsPending(false));
dispatch(getAllPrincipalGrantsError(error));
}
}
}
}

const dispatchGrantsStatus = (dispatch: Function, tenantGrantValue: IPermissionGrant[]): void => {
dispatch(getAllPrincipalGrantsPending(false));
dispatch(getAllPrincipalGrantsSuccess(tenantGrantValue));
}

const allScopesHaveConsentType = (consentedScopes: string[], tenantWideGrant: IOAuthGrantPayload, id: string) => {
const allPrincipalGrants: string[] = getAllPrincipalGrant(tenantWideGrant.value);
const singlePrincipalGrants: string[] = getSinglePrincipalGrant(tenantWideGrant.value, id);
const combinedPermissions = [...allPrincipalGrants, ...singlePrincipalGrants];
return consentedScopes.every(scope => combinedPermissions.includes(scope));
}

export const getAllPrincipalGrant = (tenantWideGrant: IPermissionGrant[]): string[] => {
if(tenantWideGrant){
const allGrants = tenantWideGrant;
if(allGrants){
const principalGrant = allGrants.find(grant => grant.consentType === 'AllPrincipals');
if(principalGrant){
return principalGrant.scope.split(' ');
}
}
}
return [];
}

export const getSinglePrincipalGrant = (tenantWideGrant: IPermissionGrant[], principalId: string): string[] => {
if(tenantWideGrant && principalId){
const allGrants = tenantWideGrant;
const singlePrincipalGrant = allGrants.find(grant => grant.principalId === principalId);
if(singlePrincipalGrant){
return singlePrincipalGrant.scope.split(' ');
}
}
return [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class RevokePermissionsUtil {
return updatedScopes;
}

public async updateAllPrincipalPermissionGrant(grantsPayload: IOAuthGrantPayload, permissionToRevoke: string) {
public async getUpdatedAllPrincipalPermissionGrant(grantsPayload: IOAuthGrantPayload, permissionToRevoke: string) {
const servicePrincipalAppId = await RevokePermissionsUtil.getServicePrincipalId([]);
const allPrincipalGrant = this.getAllPrincipalGrant(grantsPayload);
const updatedScopes = allPrincipalGrant.scope.split(' ').filter((scope: string) => scope !== permissionToRevoke);
Expand Down
26 changes: 26 additions & 0 deletions src/app/services/actions/query-action-creators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,30 @@ describe('Query action creators', () => {
})
.catch((e: Error) => { throw e });
});

it('should dispatch query status when a 401 is received', () => {
const sampleUrl = 'https://graph.microsoft.com/v1.0/me';

const store = mockStore({ graphResponse: '' });
const query = { sampleUrl }
const mockFetch = jest.fn().mockImplementation(() => {
return Promise.resolve({
ok: false,
status: 401,
body: {},
headers: [{ 'content-type': 'application-json' }]
})
});

window.fetch = mockFetch;

// @ts-ignore
return store.dispatch(runQuery(query))
.then((response) => {
expect(response.type).toBe('QUERY_GRAPH_STATUS');
expect(response.response.ok).toBe(false);
})
.catch((e: Error) => { throw e });

})
});
7 changes: 5 additions & 2 deletions src/app/services/actions/query-action-creators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ export function runQuery(query: IQuery) {

if(response && response.status === 401 && (CURRENT_RETRIES < MAX_NUMBER_OF_RETRIES)) {
const successful = await runReAuthenticatedRequest(response);
if(successful){ dispatch(runQuery(query)); }
return;
if(successful){
dispatch(runQuery(query));
return;
}
}

dispatch(setQueryResponseStatus(status));

return dispatch(
Expand Down
Loading

0 comments on commit b6dca39

Please sign in to comment.