From 277baab53d2fa75c929b86920ea192c044be654d Mon Sep 17 00:00:00 2001 From: dojcsakj Date: Mon, 25 Sep 2023 09:47:37 +0200 Subject: [PATCH 1/2] cli doctor: show scopes by resource --- src/m365/cli/commands/cli-doctor.spec.ts | 63 +++++++++++++++++++----- src/m365/cli/commands/cli-doctor.ts | 23 +++++---- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/m365/cli/commands/cli-doctor.spec.ts b/src/m365/cli/commands/cli-doctor.spec.ts index b2a8fc98baf..e54b158684f 100644 --- a/src/m365/cli/commands/cli-doctor.spec.ts +++ b/src/m365/cli/commands/cli-doctor.spec.ts @@ -69,6 +69,7 @@ describe(commands.DOCTOR, () => { it('retrieves scopes in the diagnostic information about the current environment', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', scp: 'AllSites.FullControl AppCatalog.ReadWrite.All' }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -98,18 +99,25 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: ['AllSites.FullControl', 'AppCatalog.ReadWrite.All'] + scopes: { + 'https://graph.microsoft.com': [ + 'AllSites.FullControl', + 'AppCatalog.ReadWrite.All' + ] + } })); }); it('retrieves scopes from multiple access tokens in the diagnostic information about the current environment', async () => { const jwt1 = JSON.stringify({ + aud: 'https://graph.microsoft.com', scp: 'AllSites.FullControl AppCatalog.ReadWrite.All' }); let jwt64 = Buffer.from(jwt1).toString('base64'); const accessToken1 = `abc.${jwt64}.def`; const jwt2 = JSON.stringify({ + aud: 'https://mydev.sharepoint.com', scp: 'TermStore.Read.All' }); jwt64 = Buffer.from(jwt2).toString('base64'); @@ -140,12 +148,21 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: ['AllSites.FullControl', 'AppCatalog.ReadWrite.All', 'TermStore.Read.All'] + scopes: { + 'https://graph.microsoft.com': [ + 'AllSites.FullControl', + 'AppCatalog.ReadWrite.All' + ], + 'https://mydev.sharepoint.com': [ + 'TermStore.Read.All' + ] + } })); }); it('retrieves roles in the diagnostic information about the current environment', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -175,18 +192,20 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: [] + scopes: {} })); }); it('retrieves roles from multiple access tokens in the diagnostic information about the current environment', async () => { const jwt1 = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); let jwt64 = Buffer.from(jwt1).toString('base64'); const accessToken1 = `abc.${jwt64}.def`; const jwt2 = JSON.stringify({ + aud: 'https://mydev.sharepoint.com', roles: ['TermStore.Read.All'] }); jwt64 = Buffer.from(jwt2).toString('base64'); @@ -217,12 +236,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All', 'TermStore.Read.All'], - scopes: [] + scopes: {} })); }); it('retrieves roles and scopes in the diagnostic information about the current environment', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'], scp: 'Sites.Read.All Files.ReadWrite.All' }); @@ -253,12 +273,19 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: ['Sites.Read.All', 'Files.ReadWrite.All'] + scopes: { + 'https://graph.microsoft.com': + [ + 'Sites.Read.All', + 'Files.ReadWrite.All' + ] + } })); }); it('retrieves diagnostic information about the current environment when there are no roles or scopes available', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: [], scp: '' }); @@ -290,12 +317,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: [] + scopes: {} })); }); it('retrieves diagnostic information about the current environment with auth type Certificate', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -325,12 +353,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: [] + scopes: {} })); }); it('retrieves tenant information as single when TenantID is a GUID', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -360,12 +389,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: [] + scopes: {} })); }); it('retrieves diagnostic information about the current environment (debug)', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -395,12 +425,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: [] + scopes: {} })); }); it('retrieves diagnostic information of the current environment when executing in docker', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', roles: ['Sites.Read.All', 'Files.ReadWrite.All'] }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -430,7 +461,7 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: ['Sites.Read.All', 'Files.ReadWrite.All'], - scopes: [] + scopes: {} })); }); @@ -459,7 +490,7 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: [] + scopes: {} })); }); @@ -489,12 +520,13 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: [] + scopes: {} })); }); it('retrieves CLI Configuration in the diagnostic information about the current environment', async () => { const jwt = JSON.stringify({ + aud: 'https://graph.microsoft.com', scp: 'AllSites.FullControl AppCatalog.ReadWrite.All' }); const jwt64 = Buffer.from(jwt).toString('base64'); @@ -528,7 +560,12 @@ describe(commands.DOCTOR, () => { nodeVersion: 'v14.17.0', os: { 'platform': 'win32', 'version': 'Windows 10 Pro', 'release': '10.0.19043' }, roles: [], - scopes: ['AllSites.FullControl', 'AppCatalog.ReadWrite.All'] + scopes: { + 'https://graph.microsoft.com': [ + 'AllSites.FullControl', + 'AppCatalog.ReadWrite.All' + ] + } })); }); }); diff --git a/src/m365/cli/commands/cli-doctor.ts b/src/m365/cli/commands/cli-doctor.ts index ef6d9a27aef..e915f93328c 100644 --- a/src/m365/cli/commands/cli-doctor.ts +++ b/src/m365/cli/commands/cli-doctor.ts @@ -21,7 +21,7 @@ interface CliDiagnosticInfo { cliVersion: string; cliConfig: any; roles: string[]; - scopes: string[]; + scopes: object } class CliDoctorCommand extends Command { @@ -35,13 +35,16 @@ class CliDoctorCommand extends Command { public async commandAction(logger: Logger): Promise { const roles: string[] = []; - const scopes: string[] = []; + const scopes: Map = new Map(); Object.keys(auth.service.accessTokens).forEach(resource => { const accessToken: string = auth.service.accessTokens[resource].accessToken; this.getRolesFromAccessToken(accessToken).forEach(role => roles.push(role)); - this.getScopesFromAccessToken(accessToken).forEach(scope => scopes.push(scope)); + const [res, scp] = this.getScopesFromAccessToken(accessToken); + if (res !== "") { + scopes.set(res, scp); + } }); const diagnosticInfo: CliDiagnosticInfo = { @@ -58,7 +61,7 @@ class CliDoctorCommand extends Command { cliEnvironment: process.env.CLIMICROSOFT365_ENV ? process.env.CLIMICROSOFT365_ENV : '', cliConfig: Cli.getInstance().config.all, roles: roles, - scopes: scopes + scopes: Object.fromEntries(scopes) }; await logger.log(diagnosticInfo); @@ -85,26 +88,28 @@ class CliDoctorCommand extends Command { return roles; } - private getScopesFromAccessToken(accessToken: string): string[] { + private getScopesFromAccessToken(accessToken: string): [string, string[]] { + let resource: string = ""; let scopes: string[] = []; if (!accessToken || accessToken.length === 0) { - return scopes; + return [resource, scopes]; } const chunks = accessToken.split('.'); if (chunks.length !== 3) { - return scopes; + return [resource, scopes]; } const tokenString: string = Buffer.from(chunks[1], 'base64').toString(); - const token: { scp: string } = JSON.parse(tokenString); + const token: { aud: string, scp: string } = JSON.parse(tokenString); if (token.scp?.length > 0) { + resource = token.aud; scopes = token.scp.split(' '); } - return scopes; + return [resource, scopes]; } } From 023677a6049f024f372b0452ef1ac11acdece6e4 Mon Sep 17 00:00:00 2001 From: dojcsakj Date: Mon, 16 Oct 2023 13:30:19 +0200 Subject: [PATCH 2/2] SharePoint -my and -admin entries are not shown in scopes --- src/m365/cli/commands/cli-doctor.spec.ts | 29 +++++++++++++++++++++++- src/m365/cli/commands/cli-doctor.ts | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/m365/cli/commands/cli-doctor.spec.ts b/src/m365/cli/commands/cli-doctor.spec.ts index e54b158684f..62f0def167a 100644 --- a/src/m365/cli/commands/cli-doctor.spec.ts +++ b/src/m365/cli/commands/cli-doctor.spec.ts @@ -123,9 +123,33 @@ describe(commands.DOCTOR, () => { jwt64 = Buffer.from(jwt2).toString('base64'); const accessToken2 = `abc.${jwt64}.def`; + const jwt3 = JSON.stringify({ + aud: 'https://mydev-admin.sharepoint.com', + scp: 'TermStore.Read.All' + }); + jwt64 = Buffer.from(jwt3).toString('base64'); + const accessToken3 = `abc.${jwt64}.def`; + + const jwt4 = JSON.stringify({ + aud: 'https://mydev-my.sharepoint.com', + scp: 'TermStore.Read.All' + }); + jwt64 = Buffer.from(jwt4).toString('base64'); + const accessToken4 = `abc.${jwt64}.def`; + + const jwt5 = JSON.stringify({ + aud: 'https://contoso-admin.sharepoint.com', + scp: 'TermStore.Read.All' + }); + jwt64 = Buffer.from(jwt5).toString('base64'); + const accessToken5 = `abc.${jwt64}.def`; + sinon.stub(auth.service, 'accessTokens').value({ 'https://graph.microsoft.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken1}` }, - 'https://mydev.sharepoint.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken2}` } + 'https://mydev.sharepoint.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken2}` }, + 'https://mydev-admin.sharepoint.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken3}` }, + 'https://mydev-my.sharepoint.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken4}` }, + 'https://contoso-admin.sharepoint.com': { 'expiresOn': '2021-07-04T09:52:18.000Z', 'accessToken': `${accessToken5}` } }); sinon.stub(os, 'platform').returns('win32'); sinon.stub(os, 'version').returns('Windows 10 Pro'); @@ -155,6 +179,9 @@ describe(commands.DOCTOR, () => { ], 'https://mydev.sharepoint.com': [ 'TermStore.Read.All' + ], + 'https://contoso.sharepoint.com': [ + 'TermStore.Read.All' ] } })); diff --git a/src/m365/cli/commands/cli-doctor.ts b/src/m365/cli/commands/cli-doctor.ts index e915f93328c..e4c0613dd01 100644 --- a/src/m365/cli/commands/cli-doctor.ts +++ b/src/m365/cli/commands/cli-doctor.ts @@ -105,7 +105,7 @@ class CliDoctorCommand extends Command { const token: { aud: string, scp: string } = JSON.parse(tokenString); if (token.scp?.length > 0) { - resource = token.aud; + resource = token.aud.replace(/(-my|-admin).sharepoint.com/, '.sharepoint.com'); scopes = token.scp.split(' '); }