cli-microsoft365
cli-microsoft365 copied to clipboard
Bug report: adding/setting `owner` permissions for an app on a SharePoint site throws an error
Description
The docs for spo site apppermission add
and spo site apppermission set
speak about being able to set owner permissions
https://pnp.github.io/cli-microsoft365/cmd/spo/site/site-apppermission-add/ https://pnp.github.io/cli-microsoft365/cmd/spo/site/site-apppermission-set/
But I get exceptions when trying.
Steps to reproduce
Create an app:
m365 aad app add --name 'Just an app' --apisApplication 'https://graph.microsoft.com/Sites.Selected'
Assign that app read or write permissions works:
m365 spo site apppermission add --siteUrl 'https://contoso.sharepoint.com/sites/site' --permission read --appId <some-app-id>
Assigning that app owner permissions throws an error Error: Invalid value for role
:
m365 spo site apppermission add --siteUrl 'https://contoso.sharepoint.com/sites/site' --permission owner --appId <some-app-id>
Updating read/write permissions to owner permissions throws an error Error: Invalid request
:
m365 spo site apppermission set --siteUrl 'https://contoso.sharepoint.com/sites/site' --permission owner --appId <some-app-id> --permissionId <some-permission-id>
Expected results
Owner permissions should be assigned as described.
Actual results
The command throws an error
Diagnostics
Existing access token still valid. Returning...
Request:
{
"url": "https://graph.microsoft.com/v1.0/sites/blimped.sharepoint.com:/sites/learning",
"method": "get",
"headers": {
"common": {
"Accept": "application/json, text/plain, */*"
},
"delete": {},
"get": {},
"head": {},
"post": {
"Content-Type": "application/x-www-form-urlencoded"
},
"put": {
"Content-Type": "application/x-www-form-urlencoded"
},
"patch": {
"Content-Type": "application/x-www-form-urlencoded"
},
"user-agent": "NONISV|SharePointPnP|CLIMicrosoft365/5.8.0",
"accept-encoding": "gzip, deflate",
"accept": "application/json;odata.metadata=none",
"authorization": "Bearer "
},
"responseType": "json",
"decompress": true
}
Response:
{
"url": "https://graph.microsoft.com/v1.0/sites/blimped.sharepoint.com:/sites/learning",
"status": 200,
"statusText": "OK",
"headers": {
"cache-control": "no-store, no-cache",
"transfer-encoding": "chunked",
"content-type": "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false;charset=utf-8",
"vary": "Accept-Encoding",
"strict-transport-security": "max-age=31536000",
"request-id": "7a0d9e45-1486-447a-ad47-66bf45f1f84a",
"client-request-id": "7a0d9e45-1486-447a-ad47-66bf45f1f84a",
"x-ms-ags-diagnostic": "{\"ServerInfo\":{\"DataCenter\":\"West Europe\",\"Slice\":\"E\",\"Ring\":\"5\",\"ScaleUnit\":\"004\",\"RoleInstance\":\"AM2PEPF0000BE68\"}}",
"odata-version": "4.0",
"date": "Tue, 30 Aug 2022 19:40:00 GMT",
"connection": "close"
},
"data": {
"createdDateTime": "2022-05-09T07:58:18.56Z",
"description": "",
"id": "blimped.sharepoint.com,f0f0506e-99c5-4fc0-83f5-a61cadd60713,35bf8254-4e79-4414-b8c9-b9906368aff1",
"lastModifiedDateTime": "2022-08-27T17:06:59Z",
"name": "Learning",
"webUrl": "https://blimped.sharepoint.com/sites/Learning",
"displayName": "New title",
"root": {},
"siteCollection": {
"hostname": "blimped.sharepoint.com"
}
}
}
Existing access token still valid. Returning...
Request:
{
"url": "https://graph.microsoft.com/v1.0/myorganization/applications?$filter=appId eq '4b892a20-df78-4105-81fa-cd7cfa02205f'",
"method": "get",
"headers": {
"common": {
"Accept": "application/json, text/plain, */*"
},
"delete": {},
"get": {},
"head": {},
"post": {
"Content-Type": "application/x-www-form-urlencoded"
},
"put": {
"Content-Type": "application/x-www-form-urlencoded"
},
"patch": {
"Content-Type": "application/x-www-form-urlencoded"
},
"user-agent": "NONISV|SharePointPnP|CLIMicrosoft365/5.8.0",
"accept-encoding": "gzip, deflate",
"accept": "application/json;odata.metadata=none",
"authorization": "Bearer "
},
"responseType": "json",
"decompress": true
}
Response:
{
"url": "https://graph.microsoft.com/v1.0/myorganization/applications?$filter=appId eq '4b892a20-df78-4105-81fa-cd7cfa02205f'",
"status": 200,
"statusText": "OK",
"headers": {
"cache-control": "no-cache",
"transfer-encoding": "chunked",
"content-type": "application/json;odata.metadata=none;odata.streaming=true;IEEE754Compatible=false;charset=utf-8",
"vary": "Accept-Encoding",
"strict-transport-security": "max-age=31536000",
"request-id": "0590a8a1-4401-4af4-a146-a71899d270f4",
"client-request-id": "0590a8a1-4401-4af4-a146-a71899d270f4",
"x-ms-ags-diagnostic": "{\"ServerInfo\":{\"DataCenter\":\"West Europe\",\"Slice\":\"E\",\"Ring\":\"5\",\"ScaleUnit\":\"004\",\"RoleInstance\":\"AM2PEPF0000BE6B\"}}",
"x-ms-resource-unit": "2",
"odata-version": "4.0",
"date": "Tue, 30 Aug 2022 19:40:00 GMT",
"connection": "close"
},
"data": {
"value": [
{
"id": "38b024cf-76a6-48df-83de-d206885fd5d7",
"deletedDateTime": null,
"appId": "4b892a20-df78-4105-81fa-cd7cfa02205f",
"applicationTemplateId": null,
"disabledByMicrosoftStatus": null,
"createdDateTime": "2022-08-30T19:23:54Z",
"displayName": "My awesome app no manifest",
"description": null,
"groupMembershipClaims": null,
"identifierUris": [],
"isDeviceOnlyAuthSupported": null,
"isFallbackPublicClient": null,
"notes": null,
"publisherDomain": "blimped.nl",
"serviceManagementReference": null,
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null,
"samlMetadataUrl": null,
"defaultRedirectUri": null,
"certification": null,
"optionalClaims": null,
"addIns": [],
"api": {
"acceptMappedClaims": null,
"knownClientApplications": [],
"requestedAccessTokenVersion": null,
"oauth2PermissionScopes": [],
"preAuthorizedApplications": []
},
"appRoles": [],
"info": {
"logoUrl": null,
"marketingUrl": null,
"privacyStatementUrl": null,
"supportUrl": null,
"termsOfServiceUrl": null
},
"keyCredentials": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"publicClient": {
"redirectUris": []
},
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0ff1-ce00-000000000000",
"resourceAccess": [
{
"id": "20d37865-089c-4dee-8c41-6967602d4ac8",
"type": "Role"
}
]
},
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "883ea226-0bf2-4a8f-9f9d-92c9162a727d",
"type": "Role"
}
]
}
],
"verifiedPublisher": {
"displayName": null,
"verifiedPublisherId": null,
"addedDateTime": null
},
"web": {
"homePageUrl": null,
"logoutUrl": null,
"redirectUris": [],
"implicitGrantSettings": {
"enableAccessTokenIssuance": false,
"enableIdTokenIssuance": false
}
},
"spa": {
"redirectUris": []
}
}
]
}
}
Existing access token still valid. Returning...
Request:
{
"url": "https://graph.microsoft.com/v1.0/sites/blimped.sharepoint.com,f0f0506e-99c5-4fc0-83f5-a61cadd60713,35bf8254-4e79-4414-b8c9-b9906368aff1/permissions",
"method": "post",
"headers": {
"common": {
"Accept": "application/json, text/plain, */*"
},
"delete": {},
"get": {},
"head": {},
"post": {
"Content-Type": "application/x-www-form-urlencoded"
},
"put": {
"Content-Type": "application/x-www-form-urlencoded"
},
"patch": {
"Content-Type": "application/x-www-form-urlencoded"
},
"user-agent": "NONISV|SharePointPnP|CLIMicrosoft365/5.8.0",
"accept-encoding": "gzip, deflate",
"accept": "application/json;odata.metadata=none",
"content-type": "application/json;odata=nometadata",
"authorization": "Bearer "
},
"responseType": "json",
"decompress": true,
"data": {
"roles": [
"owner"
],
"grantedToIdentities": [
{
"application": {
"id": "4b892a20-df78-4105-81fa-cd7cfa02205f",
"displayName": "My awesome app no manifest"
}
}
]
}
}
Request error:
{
"url": "https://graph.microsoft.com/v1.0/sites/blimped.sharepoint.com,f0f0506e-99c5-4fc0-83f5-a61cadd60713,35bf8254-4e79-4414-b8c9-b9906368aff1/permissions",
"status": 400,
"statusText": "Bad Request",
"headers": {
"cache-control": "no-store, no-cache",
"transfer-encoding": "chunked",
"content-type": "application/json",
"vary": "Accept-Encoding",
"strict-transport-security": "max-age=31536000",
"request-id": "fecd7060-429f-453b-a10d-af82632a262c",
"client-request-id": "fecd7060-429f-453b-a10d-af82632a262c",
"x-ms-ags-diagnostic": "{\"ServerInfo\":{\"DataCenter\":\"West Europe\",\"Slice\":\"E\",\"Ring\":\"5\",\"ScaleUnit\":\"004\",\"RoleInstance\":\"AM2PEPF0000BE3B\"}}",
"link": "<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=v1.0,Removal&from=2021-09-01&to=2021-10-01>;rel=\"deprecation\";type=\"text/html\", <https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=v1.0,Removal&from=2021-09-01&to=2021-10-01>;rel=\"deprecation\";type=\"text/html\"",
"deprecation": "Fri, 03 Sep 2021 23:59:59 GMT",
"sunset": "Sun, 03 Sep 2023 23:59:59 GMT",
"date": "Tue, 30 Aug 2022 19:40:00 GMT",
"connection": "close"
},
"error": {
"error": {
"code": "invalidRequest",
"message": "Invalid value for role",
"innerError": {
"date": "2022-08-30T19:40:01",
"request-id": "fecd7060-429f-453b-a10d-af82632a262c",
"client-request-id": "fecd7060-429f-453b-a10d-af82632a262c"
}
}
}
}
CLI for Microsoft 365 version
5.7.0
nodejs version
16.15.0
Operating system (environment)
Linux
Shell
zsh
cli doctor
{
"os": {
"platform": "linux",
"version": "#1 SMP Wed Mar 2 00:30:59 UTC 2022",
"release": "5.10.102.1-microsoft-standard-WSL2"
},
"cliVersion": "5.8.0",
"nodeVersion": "v16.15.0",
"cliAadAppId": "31359c7f-bd7e-475c-86db-fdb8c937548e",
"cliAadAppTenant": "common",
"authMode": "DeviceCode",
"cliEnvironment": "",
"cliConfig": {},
"roles": [],
"scopes": [
"AllSites.FullControl",
"AppCatalog.ReadWrite.All",
"AuditLog.Read.All",
"Bookings.Read.All",
"ChannelMember.ReadWrite.All",
"ChannelMessage.Read.All",
"ChannelMessage.Send",
"ChannelSettings.ReadWrite.All",
"Chat.Read",
"Chat.ReadWrite",
"Directory.AccessAsUser.All",
"Directory.ReadWrite.All",
"ExternalConnection.ReadWrite.All",
"Group.ReadWrite.All",
"IdentityProvider.ReadWrite.All",
"Mail.ReadWrite",
"Mail.Send",
"Notes.Read.All",
"Place.Read.All",
"Policy.Read.All",
"Reports.Read.All",
"SecurityEvents.Read.All",
"ServiceHealth.Read.All",
"ServiceMessage.Read.All",
"ServiceMessageViewpoint.Write",
"Tasks.ReadWrite",
"Team.Create",
"TeamMember.ReadWrite.All",
"TeamsApp.ReadWrite.All",
"TeamsAppInstallation.ReadWriteForUser",
"TeamSettings.ReadWrite.All",
"TeamsTab.ReadWrite.All",
"TermStore.ReadWrite.All",
"User.Invite.All",
"User.ReadWrite.All",
"profile",
"openid",
"email",
"AllSites.FullControl",
"AppCatalog.ReadWrite.All",
"AuditLog.Read.All",
"ChannelMember.ReadWrite.All",
"ChannelMessage.Read.All",
"ChannelMessage.Send",
"ChannelSettings.ReadWrite.All",
"Chat.Read",
"Chat.ReadWrite",
"Directory.AccessAsUser.All",
"Directory.ReadWrite.All",
"Group.ReadWrite.All",
"IdentityProvider.ReadWrite.All",
"Mail.ReadWrite",
"Mail.Send",
"Place.Read.All",
"Policy.Read.All",
"Reports.Read.All",
"SecurityEvents.Read.All",
"ServiceHealth.Read.All",
"ServiceMessage.Read.All",
"ServiceMessageViewpoint.Write",
"Tasks.ReadWrite",
"Team.Create",
"TeamMember.ReadWrite.All",
"TeamsApp.ReadWrite.All",
"TeamsAppInstallation.ReadWriteForUser",
"TeamSettings.ReadWrite.All",
"TeamsTab.ReadWrite.All",
"TermStore.ReadWrite.All",
"User.Invite.All",
"User.ReadWrite.All",
"AllSites.FullControl",
"AppCatalog.ReadWrite.All",
"AuditLog.Read.All",
"ChannelMember.ReadWrite.All",
"ChannelMessage.Read.All",
"ChannelMessage.Send",
"ChannelSettings.ReadWrite.All",
"Chat.Read",
"Chat.ReadWrite",
"Directory.AccessAsUser.All",
"Directory.ReadWrite.All",
"Group.ReadWrite.All",
"IdentityProvider.ReadWrite.All",
"Mail.ReadWrite",
"Mail.Send",
"Place.Read.All",
"Policy.Read.All",
"Reports.Read.All",
"SecurityEvents.Read.All",
"ServiceHealth.Read.All",
"ServiceMessage.Read.All",
"ServiceMessageViewpoint.Write",
"Tasks.ReadWrite",
"Team.Create",
"TeamMember.ReadWrite.All",
"TeamsApp.ReadWrite.All",
"TeamsAppInstallation.ReadWriteForUser",
"TeamSettings.ReadWrite.All",
"TeamsTab.ReadWrite.All",
"TermStore.ReadWrite.All",
"User.Invite.All",
"User.ReadWrite.All"
]
}
Additional Info
It may be an issue with the Graph: https://docs.microsoft.com/en-us/answers/questions/802411/issue-with-selectedsites-and-34owner34-role-sharep.html
It should be possible though: https://docs.microsoft.com/en-us/graph/api/resources/permission?view=graph-rest-1.0#roles-property-values
It seems like a bug in the API, and most likely we'll need to wait until we know more about it. Putting on hold for now
Hi @pnp/cli-for-microsoft-365-maintainers,
So I've looked into this. These are my conclusions:
- As we thought, the
owner
role cannot be granted currently. - When you create a new permission, It seems you can only add new permissions with the roles
read
andwrite
. - However: when you update existing permissions, you can use the roles
read
,write
,manage
andfullcontrol
.
That there is a difference when adding or updating permissions is odd. And that the roles differ from the MS Graph documentation is odd as well. However, it seems to be the case as it is. PnP PowerShell even has this in the documentation:
If you wish to i.e. assign FullControl permissions, you need to add read or write permissions through this cmdlet first and then update it to FullControl.
Also, checkout the documentation on the Set-PnPAzureADAppSitePermission page.
Conclusion
- It seems to be a documentation issue with Microsoft Graph.
- I doubt if the permission role
owner
has ever worked. - We should update our code so that we can assign
manage
andfullcontrol
permissions when adding and setting permissions. We can do it better than PnP PowerShell here: I'd like both commands to be able to set all roles. We'll probably need two API calls in that case. But I think that's okay.
This is a minor breaking change, but as the owner role currently is not even working, I suggest we just fix it.
Additional info
I've submitted this issue with the Graph team here: https://github.com/microsoftgraph/microsoft-graph-docs-contrib/issues/354
Thank you for the research and additional information, @martinlingstuyl. I fully agree with your suggestion that our users should be able to set whatever permission they want without being exposed to the caveats in the API, and which we should handle instead for them. Also, let's remove the erroneous owner
permission.
Thanks @waldekmastykarz, let's get this fixed and shipped 🚀 Resource Specific Consent should be configurable!