cli-microsoft365 icon indicating copy to clipboard operation
cli-microsoft365 copied to clipboard

Bug report: adding/setting `owner` permissions for an app on a SharePoint site throws an error

Open martinlingstuyl opened this issue 1 year ago • 4 comments

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

martinlingstuyl avatar Aug 30 '22 19:08 martinlingstuyl

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

waldekmastykarz avatar Sep 11 '22 12:09 waldekmastykarz

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 and write.
  • However: when you update existing permissions, you can use the roles read, write, manage and fullcontrol.

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 and fullcontrol 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

martinlingstuyl avatar Sep 12 '22 18:09 martinlingstuyl

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.

waldekmastykarz avatar Sep 15 '22 10:09 waldekmastykarz

Thanks @waldekmastykarz, let's get this fixed and shipped 🚀 Resource Specific Consent should be configurable!

martinlingstuyl avatar Sep 15 '22 10:09 martinlingstuyl