From 6efe345083e19cea2923ab3e0a047751a5ae8f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 10:25:00 +0200 Subject: [PATCH 01/43] refactor(server graphlq): migrate hasRole directive to hasServerRole --- .../typedefs/accessrequests.graphql | 6 ++-- .../activitystream/typedefs/activity.graphql | 20 +++++++---- .../server/assets/auth/typedefs/apps.graphql | 14 ++++---- .../assets/comments/typedefs/comments.gql | 18 +++++----- .../assets/core/typedefs/apitoken.graphql | 8 +++-- .../core/typedefs/branchesAndCommits.graphql | 32 ++++++++--------- .../assets/core/typedefs/server.graphql | 2 +- .../assets/core/typedefs/streams.graphql | 34 +++++++++--------- .../server/assets/core/typedefs/user.graphql | 13 +++---- .../assets/emails/typedefs/emails.graphql | 2 +- .../typedefs/notificationPreferences.graphql | 2 +- .../typedefs/serverInvites.graphql | 18 +++++----- .../assets/webhooks/typedefs/webhooks.graphql | 8 ++--- .../modules/core/graph/directives/hasRole.js | 35 ------------------- .../modules/core/graph/generated/graphql.ts | 7 ---- 15 files changed, 94 insertions(+), 125 deletions(-) diff --git a/packages/server/assets/accessrequests/typedefs/accessrequests.graphql b/packages/server/assets/accessrequests/typedefs/accessrequests.graphql index 2a8d1245d..3c9a270d9 100644 --- a/packages/server/assets/accessrequests/typedefs/accessrequests.graphql +++ b/packages/server/assets/accessrequests/typedefs/accessrequests.graphql @@ -3,7 +3,7 @@ extend type Query { Get authed user's stream access request """ streamAccessRequest(streamId: String!): StreamAccessRequest - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) } extend type Stream { @@ -21,13 +21,13 @@ extend type Mutation { requestId: String! accept: Boolean! role: StreamRole! = STREAM_CONTRIBUTOR - ): Boolean! @hasRole(role: "server:user") @hasScope(scope: "users:invite") + ): Boolean! @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") """ Request access to a specific stream """ streamAccessRequestCreate(streamId: String!): StreamAccessRequest! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") } diff --git a/packages/server/assets/activitystream/typedefs/activity.graphql b/packages/server/assets/activitystream/typedefs/activity.graphql index 807e2d4b4..c648928c6 100644 --- a/packages/server/assets/activitystream/typedefs/activity.graphql +++ b/packages/server/assets/activitystream/typedefs/activity.graphql @@ -8,7 +8,7 @@ extend type User { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasRole(role: "server:user") @hasScope(scope: "users:read") + ): ActivityCollection @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:read") """ The user's timeline in chronological order @@ -19,7 +19,7 @@ extend type User { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScopes(scopes: ["users:read", "streams:read"]) } @@ -33,7 +33,7 @@ extend type LimitedUser { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasRole(role: "server:user") @hasScope(scope: "users:read") + ): ActivityCollection @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:read") """ The user's timeline in chronological order @@ -44,7 +44,7 @@ extend type LimitedUser { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScopes(scopes: ["users:read", "streams:read"]) } @@ -58,7 +58,9 @@ extend type Stream { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasRole(role: "server:user") @hasScope(scope: "streams:read") + ): ActivityCollection + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "streams:read") } extend type Branch { @@ -71,7 +73,9 @@ extend type Branch { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasRole(role: "server:user") @hasScope(scope: "streams:read") + ): ActivityCollection + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "streams:read") } extend type Commit { @@ -84,7 +88,9 @@ extend type Commit { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasRole(role: "server:user") @hasScope(scope: "streams:read") + ): ActivityCollection + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "streams:read") } type ActivityCollection { diff --git a/packages/server/assets/auth/typedefs/apps.graphql b/packages/server/assets/auth/typedefs/apps.graphql index 591e06468..85ce003e9 100644 --- a/packages/server/assets/auth/typedefs/apps.graphql +++ b/packages/server/assets/auth/typedefs/apps.graphql @@ -47,13 +47,15 @@ extend type User { Returns the apps you have authorized. """ authorizedApps: [ServerAppListItem] - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "apps:read") """ Returns the apps you have created. """ - createdApps: [ServerApp!] @hasRole(role: "server:user") @hasScope(scope: "apps:read") + createdApps: [ServerApp!] + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "apps:read") } extend type Mutation { @@ -61,28 +63,28 @@ extend type Mutation { Register a new third party application. """ appCreate(app: AppCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "apps:write") """ Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.** """ appUpdate(app: AppUpdateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "apps:write") """ Deletes a thirty party application. """ appDelete(appId: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "apps:write") """ Revokes (de-authorizes) an application that you have previously authorized. """ appRevokeAccess(appId: String!): Boolean - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "apps:write") } diff --git a/packages/server/assets/comments/typedefs/comments.gql b/packages/server/assets/comments/typedefs/comments.gql index 90302aacd..0434b9c7d 100644 --- a/packages/server/assets/comments/typedefs/comments.gql +++ b/packages/server/assets/comments/typedefs/comments.gql @@ -334,7 +334,7 @@ extend type Mutation { resourceId: String! data: JSONObject ): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @deprecated(reason: "Use broadcastViewerUserActivity") """ @@ -345,14 +345,14 @@ extend type Mutation { commentId: String! data: JSONObject ): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @deprecated(reason: "Use broadcastViewerUserActivity") """ Creates a comment """ commentCreate(input: CommentCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -360,7 +360,7 @@ extend type Mutation { Flags a comment as viewed by you (the logged in user). """ commentView(streamId: String!, commentId: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -372,7 +372,7 @@ extend type Mutation { commentId: String! archived: Boolean! = true ): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -380,7 +380,7 @@ extend type Mutation { Edits a comment. """ commentEdit(input: CommentEditInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -388,7 +388,7 @@ extend type Mutation { Adds a reply to a comment. """ commentReply(input: ReplyCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") } @@ -457,7 +457,7 @@ extend type Subscription { - for a specific resource/set of resources: pass in a list of resourceIds (commit or object ids); this sub will get called when *any* of the resources provided get a comment. """ commentActivity(streamId: String!, resourceIds: [String]): CommentActivityMessage! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated(reason: "Use projectCommentsUpdated") @@ -470,7 +470,7 @@ extend type Subscription { streamId: String! commentId: String! ): CommentThreadActivityMessage! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") @deprecated( reason: "Use projectCommentsUpdated or viewerUserActivityBroadcasted for reply status" diff --git a/packages/server/assets/core/typedefs/apitoken.graphql b/packages/server/assets/core/typedefs/apitoken.graphql index f2fe6e3cf..187527d85 100644 --- a/packages/server/assets/core/typedefs/apitoken.graphql +++ b/packages/server/assets/core/typedefs/apitoken.graphql @@ -2,7 +2,9 @@ extend type User { """ Returns a list of your personal api tokens. """ - apiTokens: [ApiToken] @hasRole(role: "server:user") @hasScope(scope: "tokens:read") + apiTokens: [ApiToken] + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "tokens:read") } type ApiToken { @@ -26,12 +28,12 @@ extend type Mutation { Creates an personal api token. """ apiTokenCreate(token: ApiTokenCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "tokens:write") """ Revokes (deletes) an personal api token. """ apiTokenRevoke(token: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "tokens:write") } diff --git a/packages/server/assets/core/typedefs/branchesAndCommits.graphql b/packages/server/assets/core/typedefs/branchesAndCommits.graphql index 4a181bf71..435d63a65 100644 --- a/packages/server/assets/core/typedefs/branchesAndCommits.graphql +++ b/packages/server/assets/core/typedefs/branchesAndCommits.graphql @@ -48,7 +48,7 @@ type Commit { Will throw an authorization error if active user isn't authorized to see it, for example, if a stream isn't public and the user doesn't have the appropriate rights. """ - stream: Stream! @hasRole(role: "server:user") @hasScope(scope: "streams:read") + stream: Stream! @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") } type BranchCollection { @@ -65,40 +65,40 @@ type CommitCollection { extend type Mutation { branchCreate(branch: BranchCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") branchUpdate(branch: BranchUpdateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") branchDelete(branch: BranchDeleteInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") commitCreate(commit: CommitCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") commitUpdate(commit: CommitUpdateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") commitReceive(input: CommitReceivedInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") commitDelete(commit: CommitDeleteInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Move a batch of commits to a new branch """ commitsMove(input: CommitsMoveInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Delete a batch of commits """ commitsDelete(input: CommitsDeleteInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") } @@ -108,38 +108,38 @@ extend type Subscription { Subscribe to branch created event """ branchCreated(streamId: String!): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribe to branch updated event. """ branchUpdated(streamId: String!, branchId: String): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribe to branch deleted event """ branchDeleted(streamId: String!): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribe to commit created event """ commitCreated(streamId: String!): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribe to commit updated event. """ commitUpdated(streamId: String!, commitId: String): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribe to commit deleted event """ commitDeleted(streamId: String!): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/core/typedefs/server.graphql b/packages/server/assets/core/typedefs/server.graphql index 71f73a7a9..0fa9f811c 100644 --- a/packages/server/assets/core/typedefs/server.graphql +++ b/packages/server/assets/core/typedefs/server.graphql @@ -37,7 +37,7 @@ type Scope { extend type Mutation { serverInfoUpdate(info: ServerInfoUpdateInput!): Boolean - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "server:setup") } diff --git a/packages/server/assets/core/typedefs/streams.graphql b/packages/server/assets/core/typedefs/streams.graphql index ad0bdd264..c7e962d76 100644 --- a/packages/server/assets/core/typedefs/streams.graphql +++ b/packages/server/assets/core/typedefs/streams.graphql @@ -10,7 +10,7 @@ extend type Query { Pass in the `query` parameter to search by name, description or ID. """ streams(query: String, limit: Int = 25, cursor: String): StreamCollection - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ @@ -22,7 +22,7 @@ extend type Query { orderBy: String visibility: String limit: Int = 25 - ): StreamCollection @hasRole(role: "server:admin") + ): StreamCollection @hasServerRole(role: SERVER_ADMIN) """ All of the discoverable streams of the server @@ -77,7 +77,7 @@ extend type User { authenticated user, then this will only return discoverable streams. """ streams(limit: Int! = 25, cursor: String): StreamCollection! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ @@ -85,7 +85,7 @@ extend type User { Note: You can't use this to retrieve another user's favorite streams. """ favoriteStreams(limit: Int! = 25, cursor: String): StreamCollection! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ @@ -99,7 +99,7 @@ extend type LimitedUser { Returns all discoverable streams that the user is a collaborator on """ streams(limit: Int! = 25, cursor: String): StreamCollection! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ @@ -150,43 +150,43 @@ extend type Mutation { Creates a new stream. """ streamCreate(stream: StreamCreateInput!): String - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Updates an existing stream. """ streamUpdate(stream: StreamUpdateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Deletes an existing stream. """ streamDelete(id: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") - streamsDelete(ids: [String!]): Boolean! @hasRole(role: "server:admin") + streamsDelete(ids: [String!]): Boolean! @hasServerRole(role: SERVER_ADMIN) """ Update permissions of a user on a given stream. """ streamUpdatePermission(permissionParams: StreamUpdatePermissionInput!): Boolean - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Revokes the permissions of a user on a given stream. """ streamRevokePermission(permissionParams: StreamRevokePermissionInput!): Boolean - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") # Favorite/unfavorite the given stream streamFavorite(streamId: String!, favorited: Boolean!): Stream - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) """ Remove yourself from stream collaborators (not possible for the owner) """ - streamLeave(streamId: String!): Boolean! @hasRole(role: "server:user") + streamLeave(streamId: String!): Boolean! @hasServerRole(role: SERVER_USER) } extend type Subscription { @@ -200,7 +200,7 @@ extend type Subscription { **NOTE**: If someone shares a stream with you, this subscription will be triggered with an extra value of `sharedBy` in the payload. """ userStreamAdded: JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "profile:read") """ @@ -208,7 +208,7 @@ extend type Subscription { **NOTE**: If someone revokes your permissions on a stream, this subscription will be triggered with an extra value of `revokedBy` in the payload. """ userStreamRemoved: JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "profile:read") # @@ -220,14 +220,14 @@ extend type Subscription { Subscribes to stream updated event. Use this in clients/components that pertain only to this stream. """ streamUpdated(streamId: String): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") """ Subscribes to stream deleted event. Use this in clients/components that pertain only to this stream. """ streamDeleted(streamId: String): JSONObject - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/core/typedefs/user.graphql b/packages/server/assets/core/typedefs/user.graphql index 9a5a9e256..1fd08bc7f 100644 --- a/packages/server/assets/core/typedefs/user.graphql +++ b/packages/server/assets/core/typedefs/user.graphql @@ -8,7 +8,7 @@ extend type Query { Get the (limited) profile information of another server user """ otherUser(id: String!): LimitedUser - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:read") """ @@ -28,7 +28,7 @@ extend type Query { offset: Int! = 0 query: String = null ): AdminUsersListCollection - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "users:read") """ @@ -153,18 +153,19 @@ extend type Mutation { Delete a user's account. """ userDelete(userConfirmation: UserDeleteInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "profile:delete") adminDeleteUser(userConfirmation: UserDeleteInput!): Boolean! - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) - userRoleChange(userRoleInput: UserRoleInput!): Boolean! @hasRole(role: "server:admin") + userRoleChange(userRoleInput: UserRoleInput!): Boolean! + @hasServerRole(role: SERVER_ADMIN) """ Various Active User oriented mutations """ - activeUserMutations: ActiveUserMutations! @hasRole(role: "server:user") + activeUserMutations: ActiveUserMutations! @hasServerRole(role: SERVER_USER) } input UserRoleInput { diff --git a/packages/server/assets/emails/typedefs/emails.graphql b/packages/server/assets/emails/typedefs/emails.graphql index bb85a7f6c..7f601d476 100644 --- a/packages/server/assets/emails/typedefs/emails.graphql +++ b/packages/server/assets/emails/typedefs/emails.graphql @@ -9,5 +9,5 @@ extend type Mutation { """ (Re-)send the account verification e-mail """ - requestVerification: Boolean! @hasRole(role: "server:user") + requestVerification: Boolean! @hasServerRole(role: SERVER_USER) } diff --git a/packages/server/assets/notifications/typedefs/notificationPreferences.graphql b/packages/server/assets/notifications/typedefs/notificationPreferences.graphql index ccbbc8e67..a7ea36a71 100644 --- a/packages/server/assets/notifications/typedefs/notificationPreferences.graphql +++ b/packages/server/assets/notifications/typedefs/notificationPreferences.graphql @@ -4,5 +4,5 @@ extend type User { extend type Mutation { userNotificationPreferencesUpdate(preferences: JSONObject!): Boolean - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) } diff --git a/packages/server/assets/serverinvites/typedefs/serverInvites.graphql b/packages/server/assets/serverinvites/typedefs/serverInvites.graphql index 1a8305109..d58562c98 100644 --- a/packages/server/assets/serverinvites/typedefs/serverInvites.graphql +++ b/packages/server/assets/serverinvites/typedefs/serverInvites.graphql @@ -3,49 +3,49 @@ extend type Mutation { Invite a new user to the speckle server and return the invite ID """ serverInviteCreate(input: ServerInviteCreateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") """ Invite a new or registered user to the specified stream """ streamInviteCreate(input: StreamInviteCreateInput!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") serverInviteBatchCreate(input: [ServerInviteCreateInput!]!): Boolean! - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "users:invite") streamInviteBatchCreate(input: [StreamInviteCreateInput!]!): Boolean! - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "users:invite") """ Accept or decline a stream invite """ streamInviteUse(accept: Boolean!, streamId: String!, token: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) """ Cancel a pending stream invite. Can only be invoked by a stream owner. """ streamInviteCancel(streamId: String!, inviteId: String!): Boolean! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") """ Re-send a pending invite """ inviteResend(inviteId: String!): Boolean! - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "users:invite") """ Delete a pending invite """ inviteDelete(inviteId: String!): Boolean! - @hasRole(role: "server:admin") + @hasServerRole(role: SERVER_ADMIN) @hasScope(scope: "users:invite") } @@ -66,7 +66,7 @@ extend type Query { Get all invitations to streams that the active user has """ streamInvites: [PendingStreamCollaborator!]! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/webhooks/typedefs/webhooks.graphql b/packages/server/assets/webhooks/typedefs/webhooks.graphql index 8bf12f0a3..d13ea3894 100644 --- a/packages/server/assets/webhooks/typedefs/webhooks.graphql +++ b/packages/server/assets/webhooks/typedefs/webhooks.graphql @@ -1,6 +1,6 @@ extend type Stream { webhooks(id: String): WebhookCollection - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") } @@ -9,21 +9,21 @@ extend type Mutation { Creates a new webhook on a stream """ webhookCreate(webhook: WebhookCreateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Updates an existing webhook """ webhookUpdate(webhook: WebhookUpdateInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") """ Deletes an existing webhook """ webhookDelete(webhook: WebhookDeleteInput!): String! - @hasRole(role: "server:user") + @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:write") } diff --git a/packages/server/modules/core/graph/directives/hasRole.js b/packages/server/modules/core/graph/directives/hasRole.js index 8ca3292e8..985d17f3f 100644 --- a/packages/server/modules/core/graph/directives/hasRole.js +++ b/packages/server/modules/core/graph/directives/hasRole.js @@ -48,41 +48,6 @@ module.exports = { } }, - /** - * Ensure that the user has the specified SERVER role (e.g. server user, admin etc.) - * @deprecated Use `hasServerRole` instead, as it relies on proper GQL enums - * @type {import('@/modules/core/graph/helpers/directiveHelper').GraphqlDirectiveBuilder} - */ - hasRole: () => { - const directiveName = 'hasRole' - return { - typeDefs: ` - """ - Ensure that the user has the specified SERVER role (e.g. server user, admin etc.) - """ - directive @${directiveName}(role: String!) on FIELD_DEFINITION - `, - schemaTransformer: (schema) => - mapSchema(schema, { - [MapperKind.OBJECT_FIELD]: (fieldConfig) => { - const directive = getDirective(schema, fieldConfig, directiveName)?.[0] - if (!directive) return undefined - - const { role: requiredRole } = directive - const { resolve = defaultFieldResolver } = fieldConfig - fieldConfig.resolve = async function (...args) { - const context = args[2] - await validateServerRole(context, requiredRole) - - return await resolve.apply(this, args) - } - - return fieldConfig - } - }) - } - }, - /** * Ensure that the user has the specified STREAM role for a target stream (e.g. owner) * diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 6ce7d5be4..aad3bb312 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -3009,12 +3009,6 @@ export type ResolversParentTypes = { WebhookUpdateInput: WebhookUpdateInput; }; -export type HasRoleDirectiveArgs = { - role: Scalars['String']; -}; - -export type HasRoleDirectiveResolver = DirectiveResolverFn; - export type HasScopeDirectiveArgs = { scope: Scalars['String']; }; @@ -3992,7 +3986,6 @@ export type Resolvers = { }; export type DirectiveResolvers = { - hasRole?: HasRoleDirectiveResolver; hasScope?: HasScopeDirectiveResolver; hasScopes?: HasScopesDirectiveResolver; hasServerRole?: HasServerRoleDirectiveResolver; From 6ea7aa82368e08e6753270a1d341e1146ae0565e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 11:59:54 +0200 Subject: [PATCH 02/43] refactor roles to shared constants --- .../modules/auth/graph/resolvers/apps.js | 9 ++++--- .../modules/core/graph/resolvers/objects.js | 3 ++- .../modules/core/graph/resolvers/server.js | 5 ++-- .../modules/core/graph/resolvers/streams.js | 6 ++--- .../modules/core/graph/resolvers/users.js | 26 +++++++++---------- .../server/modules/core/rest/authUtils.js | 5 ++-- .../server/modules/core/services/users.js | 16 +++++++----- .../server/modules/core/tests/generic.spec.js | 12 ++++++--- .../server/modules/core/tests/graph.spec.js | 18 ++++++------- .../modules/core/tests/usersAdmin.spec.js | 11 ++++---- packages/server/modules/shared/index.js | 2 +- .../modules/stats/graph/resolvers/stats.js | 3 ++- .../server/scripts/duplicateUserMigration.js | 5 ++-- 13 files changed, 67 insertions(+), 54 deletions(-) diff --git a/packages/server/modules/auth/graph/resolvers/apps.js b/packages/server/modules/auth/graph/resolvers/apps.js index ca72cc9b4..a8902700b 100644 --- a/packages/server/modules/auth/graph/resolvers/apps.js +++ b/packages/server/modules/auth/graph/resolvers/apps.js @@ -11,6 +11,7 @@ const { deleteApp, revokeExistingAppCredentialsForUser } = require('../../services/apps') +const { Roles } = require('@speckle/shared') module.exports = { Query: { @@ -56,10 +57,10 @@ module.exports = { async appUpdate(parent, args, context) { const app = await getApp({ id: args.app.id }) // only admins can update the default apps, generated by the server - if (!app.author && context.role !== 'server:admin') + if (!app.author && context.role !== Roles.Server.Admin) throw new ForbiddenError('You are not authorized to edit this app.') // only the author or an admin can update a 3rd party app - if (app.author.id !== context.userId && context.role !== 'server:admin') + if (app.author.id !== context.userId && context.role !== Roles.Server.Admin) throw new ForbiddenError('You are not authorized to edit this app.') await updateApp({ app: args.app }) @@ -69,9 +70,9 @@ module.exports = { async appDelete(parent, args, context) { const app = await getApp({ id: args.appId }) - if (!app.author && context.role !== 'server:admin') + if (!app.author && context.role !== Roles.Server.Admin) throw new ForbiddenError('You are not authorized to edit this app.') - if (app.author.id !== context.userId && context.role !== 'server:admin') + if (app.author.id !== context.userId && context.role !== Roles.Server.Admin) throw new ForbiddenError('You are not authorized to edit this app.') return (await deleteApp({ id: args.appId })) === 1 diff --git a/packages/server/modules/core/graph/resolvers/objects.js b/packages/server/modules/core/graph/resolvers/objects.js index 6528a5543..5b76bfcba 100644 --- a/packages/server/modules/core/graph/resolvers/objects.js +++ b/packages/server/modules/core/graph/resolvers/objects.js @@ -11,6 +11,7 @@ const { getObjectChildren, getObjectChildrenQuery } = require('../../services/objects') +const { Roles } = require('@speckle/shared') module.exports = { Stream: { @@ -59,7 +60,7 @@ module.exports = { }, Mutation: { async objectCreate(parent, args, context) { - await validateServerRole(context, 'server:user') + await validateServerRole(context, Roles.Server.User) await validateScopes(context.scopes, 'streams:write') await authorizeResolver( context.userId, diff --git a/packages/server/modules/core/graph/resolvers/server.js b/packages/server/modules/core/graph/resolvers/server.js index ad745b555..e9fbffa38 100644 --- a/packages/server/modules/core/graph/resolvers/server.js +++ b/packages/server/modules/core/graph/resolvers/server.js @@ -6,6 +6,7 @@ const { getPublicScopes, getPublicRoles } = require('../../services/generic') +const { Roles, Scopes } = require('@speckle/shared') module.exports = { Query: { @@ -26,8 +27,8 @@ module.exports = { Mutation: { async serverInfoUpdate(parent, args, context) { - await validateServerRole(context, 'server:admin') - await validateScopes(context.scopes, 'server:setup') + await validateServerRole(context, Roles.Server.Admin) + await validateScopes(context.scopes, Scopes.Server.Setup) await updateServerInfo(args.info) return true diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index add5e9d7e..1261afff4 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -48,7 +48,7 @@ const { updateStreamRoleAndNotify } = require('@/modules/core/services/streams/management') const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') const { StreamNotFoundError } = require('@/modules/core/errors/stream') // subscription events @@ -88,8 +88,8 @@ module.exports = { await authorizeResolver(context.userId, args.id, 'stream:reviewer') if (!stream.isPublic) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:read') + await validateServerRole(context, Roles.Server.User) + await validateScopes(context.scopes, Scopes.Streams.Read) } return stream diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 71d9084f8..2a26a808c 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -18,7 +18,7 @@ const zxcvbn = require('zxcvbn') const { getAdminUsersListCollection } = require('@/modules/core/services/users/adminUsersListService') -const { Roles, Scopes } = require('@/modules/core/helpers/mainConstants') +const { Roles, Scopes } = require('@speckle/shared') const { markOnboardingComplete } = require('@/modules/core/repositories/users') const { UsersMeta } = require('@/modules/core/dbSchema') @@ -33,8 +33,8 @@ module.exports = { if (!activeUserId) return null // Only if authenticated - check for server roles & scopes - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'profile:read') + await validateServerRole(context, Roles.Server.User) + await validateScopes(context.scopes, Scopes.Profile.Read) return await getUser(activeUserId) }, @@ -47,10 +47,10 @@ module.exports = { // User wants info about himself and he's not authenticated - just return null if (!context.auth && !args.id) return null - await validateServerRole(context, 'server:user') + await validateServerRole(context, Roles.Server.User) - if (!args.id) await validateScopes(context.scopes, 'profile:read') - else await validateScopes(context.scopes, 'users:read') + if (!args.id) await validateScopes(context.scopes, Scopes.Profile.Read) + else await validateScopes(context.scopes, Scopes.Users.Read) if (!args.id && !context.userId) { throw new UserInputError('You must provide an user id.') @@ -64,7 +64,7 @@ module.exports = { }, async userSearch(parent, args, context) { - await validateServerRole(context, 'server:user') + await validateServerRole(context, Roles.Server.User) await validateScopes(context.scopes, 'profile:read') await validateScopes(context.scopes, 'users:read') @@ -131,16 +131,16 @@ module.exports = { }, Mutation: { async userUpdate(parent, args, context) { - await validateServerRole(context, 'server:user') + await validateServerRole(context, Roles.Server.User) await updateUserAndNotify(context.userId, args.user) return true }, async userRoleChange(parent, args) { const roleChangers = { - 'server:admin': makeUserAdmin, - 'server:user': unmakeUserAdmin, - 'server:archived-user': archiveUser + [Roles.Server.Admin]: makeUserAdmin, + [Roles.Server.User]: unmakeUserAdmin, + [Roles.Server.ArchivedUser]: archiveUser } const roleChanger = roleChangers[args.userRoleInput.role] await roleChanger({ userId: args.userRoleInput.id }) @@ -148,7 +148,7 @@ module.exports = { }, async adminDeleteUser(parent, args, context) { - await validateServerRole(context, 'server:admin') + await validateServerRole(context, Roles.Server.Admin) const user = await getUserByEmail({ email: args.userConfirmation.email }) await deleteUser(user.id) return true @@ -164,7 +164,7 @@ module.exports = { // The below are not really needed anymore as we've added the hasRole and hasScope // directives in the graphql schema itself. // Since I am paranoid, I'll leave them here too. - await validateServerRole(context, 'server:user') + await validateServerRole(context, Roles.Server.User) await validateScopes(context.scopes, 'profile:delete') await deleteUser(context.userId, args.user) diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index 186a6ff02..9937c1deb 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -6,6 +6,7 @@ const { } = require('@/modules/shared') const { getStream } = require('../services/streams') +const { Roles } = require('@speckle/shared') module.exports = { async validatePermissionsReadStream(streamId, req) { @@ -13,7 +14,7 @@ module.exports = { if (stream?.isPublic) return { result: true, status: 200 } try { - await validateServerRole(req.context, 'server:user') + await validateServerRole(req.context, Roles.Server.User) } catch (err) { return { result: false, status: 401 } } @@ -46,7 +47,7 @@ module.exports = { } try { - await validateServerRole(req.context, 'server:user') + await validateServerRole(req.context, Roles.Server.User) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index 1ddc350f3..f6f7e5efe 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -26,17 +26,18 @@ const { UserInputError, PasswordTooShortError } = require('@/modules/core/errors/userinput') +const { Roles } = require('@speckle/shared') const changeUserRole = async ({ userId, role }) => await Acl().where({ userId }).update({ role }) const countAdminUsers = async () => { - const [{ count }] = await Acl().where({ role: 'server:admin' }).count() + const [{ count }] = await Acl().where({ role: Roles.Server.Admin }).count() return parseInt(count) } const _ensureAtleastOneAdminRemains = async (userId) => { if ((await countAdminUsers()) === 1) { - const currentAdmin = await Acl().where({ role: 'server:admin' }).first() + const currentAdmin = await Acl().where({ role: Roles.Server.Admin }).first() if (currentAdmin.userId === userId) { throw new UserInputError('Cannot remove the last admin role from the server') } @@ -93,7 +94,8 @@ module.exports = { const [newUser] = (await Users().insert(user, UsersSchema.cols)) || [] if (!newUser) throw new Error("Couldn't create user") - const userRole = (await countAdminUsers()) === 0 ? 'server:admin' : 'server:user' + const userRole = + (await countAdminUsers()) === 0 ? Roles.Server.Admin : Roles.Server.User await Acl().insert({ userId: newId, role: userRole }) @@ -182,7 +184,7 @@ module.exports = { .where((queryBuilder) => { queryBuilder.where({ email: searchQuery }) //match full email or partial name if (!emailOnly) queryBuilder.orWhere('name', 'ILIKE', `%${searchQuery}%`) - if (!archived) queryBuilder.andWhere('role', '!=', 'server:archived-user') + if (!archived) queryBuilder.andWhere('role', '!=', Roles.Server.ArchivedUser) }) if (cursor) query.andWhere('users.createdAt', '<', cursor) @@ -263,19 +265,19 @@ module.exports = { }, async makeUserAdmin({ userId }) { - await changeUserRole({ userId, role: 'server:admin' }) + await changeUserRole({ userId, role: Roles.Server.Admin }) }, async unmakeUserAdmin({ userId }) { // dont delete last admin role await _ensureAtleastOneAdminRemains(userId) - await changeUserRole({ userId, role: 'server:user' }) + await changeUserRole({ userId, role: Roles.Server.User }) }, async archiveUser({ userId }) { // dont change last admin to archived await _ensureAtleastOneAdminRemains(userId) - await changeUserRole({ userId, role: 'server:archived-user' }) + await changeUserRole({ userId, role: Roles.Server.ArchivedUser }) }, async countUsers(searchQuery = null) { diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index 80364134f..4763af0fc 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -20,6 +20,7 @@ const { } = require('@/modules/shared') const { buildContext } = require('@/modules/shared/middleware') const { ForbiddenError } = require('apollo-server-express') +const { Roles } = require('@speckle/shared') describe('Generic AuthN & AuthZ controller tests', () => { before(async () => { @@ -60,7 +61,10 @@ describe('Generic AuthN & AuthZ controller tests', () => { ) it('Should validate server role', async () => { - await validateServerRole({ auth: true, role: 'server:user' }, 'server:admin') + await validateServerRole( + { auth: true, role: Roles.Server.User }, + Roles.Server.Admin + ) .then(() => { throw new Error('This should have been rejected') }) @@ -74,15 +78,15 @@ describe('Generic AuthN & AuthZ controller tests', () => { }) .catch((err) => expect('Invalid server role specified').to.equal(err.message)) - await validateServerRole({ auth: true, role: 'server:admin' }, '133TCR3w') + await validateServerRole({ auth: true, role: Roles.Server.Admin }, '133TCR3w') .then(() => { throw new Error('This should have been rejected') }) .catch((err) => expect('Invalid server role specified').to.equal(err.message)) const test = await validateServerRole( - { auth: true, role: 'server:admin' }, - 'server:user' + { auth: true, role: Roles.Server.Admin }, + Roles.Server.User ) expect(test).to.equal(true) }) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index ecbcd1528..2da700ff8 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -11,7 +11,7 @@ const { addOrUpdateStreamCollaborator, removeStreamCollaborator } = require('@/modules/core/services/streams/streamAccessService') -const { Roles } = require('@/modules/core/helpers/mainConstants') +const { Roles } = require('@speckle/shared') let app let server @@ -303,30 +303,30 @@ describe('GraphQL API Core @core-api', () => { let queriedUserB = await sendRequest(userA.token, { query: ` { otherUser(id:"${userB.id}") { id name role } }` }) - expect(queriedUserB.body.data.otherUser.role).to.equal('server:user') - let query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "server:admin"})}` + expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User) + let query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.Admin}"})}` await sendRequest(userA.token, { query }) queriedUserB = await sendRequest(userA.token, { query: ` { otherUser(id:"${userB.id}") { id name role } }` }) - expect(queriedUserB.body.data.otherUser.role).to.equal('server:admin') + expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.Admin) expect(queriedUserB.body.data) - query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "server:user"})}` + query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.User}"})}` await sendRequest(userA.token, { query }) queriedUserB = await sendRequest(userA.token, { query: ` { otherUser(id:"${userB.id}") { id name role } }` }) - expect(queriedUserB.body.data.otherUser.role).to.equal('server:user') + expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User) }) it('Only admins can change user role', async () => { - const query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "server:admin"})}` + const query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.Admin}"})}` const res = await sendRequest(userB.token, { query }) const queriedUserB = await sendRequest(userA.token, { query: ` { otherUser(id:"${userB.id}") { id name role } }` }) expect(res.body.errors).to.exist - expect(queriedUserB.body.data.otherUser.role).to.equal('server:user') + expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User) }) }) @@ -1054,7 +1054,7 @@ describe('GraphQL API Core @core-api', () => { expect(res.body.data).to.have.property('user') expect(res.body.data.user.name).to.equal('MiticÄ') expect(res.body.data.user.email).to.equal('d.1@speckle.systems') - expect(res.body.data.user.role).to.equal('server:admin') + expect(res.body.data.user.role).to.equal(Roles.Server.Admin) }) it('Should retrieve my streams', async () => { diff --git a/packages/server/modules/core/tests/usersAdmin.spec.js b/packages/server/modules/core/tests/usersAdmin.spec.js index dcdb67ed9..082961a03 100644 --- a/packages/server/modules/core/tests/usersAdmin.spec.js +++ b/packages/server/modules/core/tests/usersAdmin.spec.js @@ -11,6 +11,7 @@ const { makeUserAdmin } = require('../services/users') const { beforeEachContext } = require('@/test/hooks') +const { Roles } = require('@speckle/shared') describe('User admin @user-services', () => { const myTestActor = { @@ -33,7 +34,7 @@ describe('User admin @user-services', () => { const firstUser = users[0] const userRole = await getUserRole(firstUser.id) - expect(userRole).to.equal('server:admin') + expect(userRole).to.equal(Roles.Server.Admin) }) it('Count user knows how to count', async () => { @@ -93,20 +94,20 @@ describe('User admin @user-services', () => { const [user] = await getUsers(1, 10) const oldRole = await getUserRole(user.id) - expect(oldRole).to.equal('server:user') + expect(oldRole).to.equal(Roles.Server.User) await makeUserAdmin({ userId: user.id }) let newRole = await getUserRole(user.id) - expect(newRole).to.equal('server:admin') + expect(newRole).to.equal(Roles.Server.Admin) await unmakeUserAdmin({ userId: user.id }) newRole = await getUserRole(user.id) - expect(newRole).to.equal('server:user') + expect(newRole).to.equal(Roles.Server.User) }) it('Ensure at least one admin remains in the server', async () => { try { - await unmakeUserAdmin({ userId: myTestActor.id, role: 'server:admin' }) + await unmakeUserAdmin({ userId: myTestActor.id, role: Roles.Server.Admin }) assert.fail('This should have failed') } catch (err) { expect(err.message).to.equal('Cannot remove the last admin role from the server') diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index f0f027354..db2c8d31e 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -38,7 +38,7 @@ async function validateServerRole(context, requiredRole) { if (!myRole) throw new ForbiddenError('You do not have the required server role (null)') - if (context.role === 'server:admin') return true + if (context.role === Roles.Server.Admin) return true if (myRole.weight >= role.weight) return true throw new ForbiddenError('You do not have the required server role') diff --git a/packages/server/modules/stats/graph/resolvers/stats.js b/packages/server/modules/stats/graph/resolvers/stats.js index 5cf49e8ab..e8e9fcd08 100644 --- a/packages/server/modules/stats/graph/resolvers/stats.js +++ b/packages/server/modules/stats/graph/resolvers/stats.js @@ -10,11 +10,12 @@ const { getTotalObjectCount, getTotalUserCount } = require('../../services') +const { Roles } = require('@speckle/shared') module.exports = { Query: { async serverStats(parent, args, context) { - await validateServerRole(context, 'server:admin') + await validateServerRole(context, Roles.Server.Admin) await validateScopes(context.scopes, 'server:stats') return {} } diff --git a/packages/server/scripts/duplicateUserMigration.js b/packages/server/scripts/duplicateUserMigration.js index c18946e37..df59462e6 100644 --- a/packages/server/scripts/duplicateUserMigration.js +++ b/packages/server/scripts/duplicateUserMigration.js @@ -1,6 +1,7 @@ const knex = require('@/db/knex') const { logger } = require('@/logging/logging') const roles = require('@/modules/core/roles.js') +const { Roles } = require('@speckle/shared') const Users = () => knex('users') @@ -35,10 +36,10 @@ const migrateColumnValue = async (tableName, columnName, oldUser, newUser) => { const serverAclMigration = async ({ lowerUser, upperUser }) => { const oldAcl = await knex('server_acl').where({ userId: upperUser.id }).first() // if the old user was admin, make the target admin too - if (oldAcl.role === 'server:admin') + if (oldAcl.role === Roles.Server.Admin) await knex('server_acl') .where({ userId: lowerUser.id }) - .update({ role: 'server:admin' }) + .update({ role: Roles.Server.Admin }) } const _migrateSingleStreamAccess = async ({ lowerUser, upperStreamAcl }) => { From bb5778bf2d50208de2daf7b2ae4e09a294d21072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:09:34 +0200 Subject: [PATCH 03/43] refactor(server scopes): use constants for streams read scopes --- packages/fileimport-service/src/daemon.js | 3 ++- .../activitystream/tests/activity.spec.js | 6 +++--- .../modules/auth/tests/appsGrapql.spec.js | 11 +++++----- .../server/modules/core/rest/authUtils.js | 4 ++-- .../server/modules/core/tests/generic.spec.js | 4 ++-- .../server/modules/core/tests/graph.spec.js | 20 +++++++++---------- .../modules/core/tests/graphSubs.spec.js | 8 ++++---- .../server/modules/core/tests/rest.spec.js | 5 +++-- .../server/modules/core/tests/users.spec.js | 7 ++++--- packages/server/modules/previews/index.js | 3 ++- .../modules/stats/graph/resolvers/stats.js | 4 ++-- .../server/modules/stats/tests/stats.spec.js | 9 +++++---- .../modules/webhooks/tests/webhooks.spec.js | 5 +++-- packages/server/scripts/streamObjects.js | 3 ++- 14 files changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/fileimport-service/src/daemon.js b/packages/fileimport-service/src/daemon.js index bfe9e3baa..11b041982 100644 --- a/packages/fileimport-service/src/daemon.js +++ b/packages/fileimport-service/src/daemon.js @@ -17,6 +17,7 @@ const { spawn } = require('child_process') const ServerAPI = require('../ifc/api') const objDependencies = require('./objDependencies') const { logger } = require('../observability/logging') +const { Scopes } = require('@speckle/shared') const HEALTHCHECK_FILE_PATH = '/tmp/last_successful_query' @@ -97,7 +98,7 @@ async function doTask(task) { const { token } = await serverApi.createToken({ userId: info.userId, name: 'temp upload token', - scopes: ['streams:write', 'streams:read'], + scopes: ['streams:write', Scopes.Streams.Read], lifespan: 1000000 }) tempUserToken = token diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index c4b9d9211..b3b1c05bf 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -11,7 +11,7 @@ const { noErrors } = require('@/test/helpers') const { addOrUpdateStreamCollaborator } = require('@/modules/core/services/streams/streamAccessService') -const { Roles } = require('@/modules/core/helpers/mainConstants') +const { Roles, Scopes } = require('@speckle/shared') let sendRequest @@ -79,7 +79,7 @@ describe('Activity @activity', () => { ;({ sendRequest } = await initializeTestServer(server, app)) const normalScopesList = [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -106,7 +106,7 @@ describe('Activity @activity', () => { (token) => (userCr.token = `Bearer ${token}`) ), createPersonalAccessToken(userX.id, 'no users:read test token', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write' ]).then((token) => (userX.token = `Bearer ${token}`)) // streams diff --git a/packages/server/modules/auth/tests/appsGrapql.spec.js b/packages/server/modules/auth/tests/appsGrapql.spec.js index b88de762d..76f307119 100644 --- a/packages/server/modules/auth/tests/appsGrapql.spec.js +++ b/packages/server/modules/auth/tests/appsGrapql.spec.js @@ -11,6 +11,7 @@ const { createAuthorizationCode, createAppTokenFromAccessCode } = require('../services/apps') +const { Scopes } = require('@speckle/shared') let sendRequest let server @@ -67,7 +68,7 @@ describe('GraphQL @apps-api', () => { name: 'Test App', public: true, description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } @@ -88,7 +89,7 @@ describe('GraphQL @apps-api', () => { myApp: { name: 'Test App', description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } @@ -160,7 +161,7 @@ describe('GraphQL @apps-api', () => { id: testAppId, name: 'Updated Test App', description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } @@ -192,7 +193,7 @@ describe('GraphQL @apps-api', () => { name: 'Another Test App', public: false, description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } @@ -203,7 +204,7 @@ describe('GraphQL @apps-api', () => { name: 'The n-th Test App', public: false, description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index 9937c1deb..f2b503648 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -6,7 +6,7 @@ const { } = require('@/modules/shared') const { getStream } = require('../services/streams') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') module.exports = { async validatePermissionsReadStream(streamId, req) { @@ -27,7 +27,7 @@ module.exports = { if (!stream.isPublic) { try { - await validateScopes(req.context.scopes, 'streams:read') + await validateScopes(req.context.scopes, Scopes.Streams.Read) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index 4763af0fc..2c4bb95f6 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -20,7 +20,7 @@ const { } = require('@/modules/shared') const { buildContext } = require('@/modules/shared/middleware') const { ForbiddenError } = require('apollo-server-express') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') describe('Generic AuthN & AuthZ controller tests', () => { before(async () => { @@ -99,7 +99,7 @@ describe('Generic AuthN & AuthZ controller tests', () => { .catch((err) => expect('Unknown role: bar').to.equal(err.message)) // this caught me out, but streams:read is not a valid role for now - await authorizeResolver('foo', 'bar', 'streams:read') + await authorizeResolver('foo', 'bar', Scopes.Streams.Read) .then(() => { throw new Error('This should have been rejected') }) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 2da700ff8..299d27064 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -11,7 +11,7 @@ const { addOrUpdateStreamCollaborator, removeStreamCollaborator } = require('@/modules/core/services/streams/streamAccessService') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') let app let server @@ -44,8 +44,8 @@ describe('GraphQL API Core @core-api', () => { userA.id, 'test token user A', [ - 'server:setup', - 'streams:read', + Scopes.Server.Setup, + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -60,7 +60,7 @@ describe('GraphQL API Core @core-api', () => { userB.id, 'test token user B', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -75,7 +75,7 @@ describe('GraphQL API Core @core-api', () => { userC.id, 'test token user B', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -243,7 +243,7 @@ describe('GraphQL API Core @core-api', () => { userDelete.id, 'fail token user del', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -271,7 +271,7 @@ describe('GraphQL API Core @core-api', () => { userDelete.id, 'test token user del', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -1693,7 +1693,7 @@ describe('GraphQL API Core @core-api', () => { archivedUser.id, 'this will be archived', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -1735,7 +1735,7 @@ describe('GraphQL API Core @core-api', () => { query, variables: { tokenInput: { - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], name: 'thisWillNotBeCreated', lifespan: 1000000 } @@ -1840,7 +1840,7 @@ describe('GraphQL API Core @core-api', () => { name: 'Test App', public: true, description: 'Test App Description', - scopes: ['streams:read'], + scopes: [Scopes.Streams.Read], redirectUrl: 'lol://what' } } diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 57cfa61ce..4ebb659ff 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -17,7 +17,7 @@ const { packageRoot } = require('@/bootstrap') const { addOrUpdateStreamCollaborator } = require('@/modules/core/services/streams/streamAccessService') -const { Roles } = require('@/modules/core/helpers/mainConstants') +const { Roles, Scopes } = require('@speckle/shared') const { getFreeServerPort } = require('@/test/serverHelper') let addr @@ -100,7 +100,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userA.id = await createUser(userA) const token = await createPersonalAccessToken(userA.id, 'test token user A', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -116,7 +116,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userB.id, 'test token user B', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -131,7 +131,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userC.token = `Bearer ${await createPersonalAccessToken( userC.id, 'test token user B', - ['streams:read', 'streams:write', 'users:read', 'users:email'] + [Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email'] )}` }) diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index d0f17769c..916dcf5ef 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -11,6 +11,7 @@ const { createManyObjects } = require('@/test/helpers') const { createUser } = require('../services/users') const { createPersonalAccessToken } = require('../services/tokens') const { createStream } = require('../services/streams') +const { Scopes } = require('@speckle/shared') describe('Upload/Download Routes @api-rest', () => { const userA = { @@ -40,7 +41,7 @@ describe('Upload/Download Routes @api-rest', () => { userA.id, 'test token user A', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', @@ -56,7 +57,7 @@ describe('Upload/Download Routes @api-rest', () => { userB.id, 'test token user B', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 915f0a94f..923ad0781 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -41,6 +41,7 @@ const { const { createObject } = require('../services/objects') const { beforeEachContext } = require('@/test/hooks') +const { Scopes } = require('@speckle/shared') describe('Actors & Tokens @user-services', () => { const myTestActor = { @@ -368,18 +369,18 @@ describe('Actors & Tokens @user-services', () => { before(async () => { pregeneratedToken = await createPersonalAccessToken(myTestActor.id, 'Whabadub', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'profile:read', 'users:email' ]) revokedToken = await createPersonalAccessToken(myTestActor.id, 'Mr. Revoked', [ - 'streams:read' + Scopes.Streams.Read ]) expireSoonToken = await createPersonalAccessToken( myTestActor.id, 'Mayfly', - ['streams:read'], + [Scopes.Streams.Read], 1 ) // 1ms lifespan }) diff --git a/packages/server/modules/previews/index.js b/packages/server/modules/previews/index.js index c3945d52f..22e6319b6 100644 --- a/packages/server/modules/previews/index.js +++ b/packages/server/modules/previews/index.js @@ -21,6 +21,7 @@ const { moduleLogger, logger } = require('@/logging/logging') const { listenForPreviewGenerationUpdates } = require('@/modules/previews/services/resultListener') +const { Scopes } = require('@speckle/shared') const httpErrorImage = (httpErrorCode) => require.resolve(`#/assets/previews/images/preview_${httpErrorCode}.png`) @@ -144,7 +145,7 @@ exports.init = (app) => { if (!stream.isPublic) { try { - await validateScopes(req.context.scopes, 'streams:read') + await validateScopes(req.context.scopes, Scopes.Streams.Read) } catch (err) { return { hasPermissions: false, httpErrorCode: 401 } } diff --git a/packages/server/modules/stats/graph/resolvers/stats.js b/packages/server/modules/stats/graph/resolvers/stats.js index e8e9fcd08..7ac09896f 100644 --- a/packages/server/modules/stats/graph/resolvers/stats.js +++ b/packages/server/modules/stats/graph/resolvers/stats.js @@ -10,13 +10,13 @@ const { getTotalObjectCount, getTotalUserCount } = require('../../services') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') module.exports = { Query: { async serverStats(parent, args, context) { await validateServerRole(context, Roles.Server.Admin) - await validateScopes(context.scopes, 'server:stats') + await validateScopes(context.scopes, Scopes.Server.Stats) return {} } }, diff --git a/packages/server/modules/stats/tests/stats.spec.js b/packages/server/modules/stats/tests/stats.spec.js index 8758371f3..122ff1a50 100644 --- a/packages/server/modules/stats/tests/stats.spec.js +++ b/packages/server/modules/stats/tests/stats.spec.js @@ -20,6 +20,7 @@ const { getTotalObjectCount, getTotalUserCount } = require('../services') +const { Scopes } = require('@speckle/shared') const params = { numUsers: 25, numStreams: 30, numObjects: 100, numCommits: 100 } @@ -126,24 +127,24 @@ describe('Server stats api @stats-api', function () { adminUser.goodToken = `Bearer ${await createPersonalAccessToken( adminUser.id, 'test token user A', - ['server:stats'] + [Scopes.Server.Stats] )}` adminUser.badToken = `Bearer ${await createPersonalAccessToken( adminUser.id, 'test token user A', - ['streams:read'] + [Scopes.Streams.Read] )}` notAdminUser.id = await createUser(notAdminUser) notAdminUser.goodToken = `Bearer ${await createPersonalAccessToken( notAdminUser.id, 'test token user A', - ['server:stats'] + [Scopes.Server.Stats] )}` notAdminUser.badToken = `Bearer ${await createPersonalAccessToken( notAdminUser.id, 'test token user A', - ['streams:read'] + [Scopes.Streams.Read] )}` await seedDb(params) diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index 14ec5f62e..d160073a2 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -16,6 +16,7 @@ const { } = require('../services/webhooks') const { createUser } = require('../../core/services/users') const { createStream, grantPermissionsStream } = require('../../core/services/streams') +const { Scopes } = require('@speckle/shared') describe('Webhooks @webhooks', () => { let server, sendRequest, app @@ -139,12 +140,12 @@ describe('Webhooks @webhooks', () => { userOne.token = `Bearer ${await createPersonalAccessToken( userOne.id, 'userOne test token', - ['streams:read', 'streams:write'] + [Scopes.Streams.Read, 'streams:write'] )}` userTwo.token = `Bearer ${await createPersonalAccessToken( userTwo.id, 'userTwo test token', - ['streams:read', 'streams:write'] + [Scopes.Streams.Read, 'streams:write'] )}` await grantPermissionsStream({ streamId: streamTwo.id, diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index b1c6c1e53..fce314e56 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -9,6 +9,7 @@ const { init } = require(`@/app`) const request = require('supertest') const { exit } = require('yargs') const { logger } = require('@/logging/logging') +const { Scopes } = require('@speckle/shared') const main = async () => { const testStream = { @@ -30,7 +31,7 @@ const main = async () => { userA.id, 'test token user A', [ - 'streams:read', + Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email', From 878f97baa2c0af9e21741e9139b2502ab5bf8f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:13:09 +0200 Subject: [PATCH 04/43] refactor(server scopes): use constants for streams write scopes --- packages/fileimport-service/src/daemon.js | 2 +- .../modules/activitystream/tests/activity.spec.js | 4 ++-- .../server/modules/core/graph/resolvers/objects.js | 4 ++-- packages/server/modules/core/rest/authUtils.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 6 +++--- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/modules/core/tests/users.spec.js | 4 ++-- .../server/modules/webhooks/tests/webhooks.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/fileimport-service/src/daemon.js b/packages/fileimport-service/src/daemon.js index 11b041982..f4959519a 100644 --- a/packages/fileimport-service/src/daemon.js +++ b/packages/fileimport-service/src/daemon.js @@ -98,7 +98,7 @@ async function doTask(task) { const { token } = await serverApi.createToken({ userId: info.userId, name: 'temp upload token', - scopes: ['streams:write', Scopes.Streams.Read], + scopes: [Scopes.Streams.Write, Scopes.Streams.Read], lifespan: 1000000 }) tempUserToken = token diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index b3b1c05bf..972a18c11 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -80,7 +80,7 @@ describe('Activity @activity', () => { const normalScopesList = [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -107,7 +107,7 @@ describe('Activity @activity', () => { ), createPersonalAccessToken(userX.id, 'no users:read test token', [ Scopes.Streams.Read, - 'streams:write' + Scopes.Streams.Write ]).then((token) => (userX.token = `Bearer ${token}`)) // streams // createStream({ ...collaboratorTestStream, ownerId: userIz.id }).then( diff --git a/packages/server/modules/core/graph/resolvers/objects.js b/packages/server/modules/core/graph/resolvers/objects.js index 5b76bfcba..faeb878bd 100644 --- a/packages/server/modules/core/graph/resolvers/objects.js +++ b/packages/server/modules/core/graph/resolvers/objects.js @@ -11,7 +11,7 @@ const { getObjectChildren, getObjectChildrenQuery } = require('../../services/objects') -const { Roles } = require('@speckle/shared') +const { Roles, Scopes } = require('@speckle/shared') module.exports = { Stream: { @@ -61,7 +61,7 @@ module.exports = { Mutation: { async objectCreate(parent, args, context) { await validateServerRole(context, Roles.Server.User) - await validateScopes(context.scopes, 'streams:write') + await validateScopes(context.scopes, Scopes.Streams.Write) await authorizeResolver( context.userId, args.objectInput.streamId, diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index f2b503648..b7c95e24d 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -53,7 +53,7 @@ module.exports = { } try { - await validateScopes(req.context.scopes, 'streams:write') + await validateScopes(req.context.scopes, Scopes.Streams.Write) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 299d27064..02dd9cd49 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -46,7 +46,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Server.Setup, Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -61,7 +61,7 @@ describe('GraphQL API Core @core-api', () => { 'test token user B', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -76,7 +76,7 @@ describe('GraphQL API Core @core-api', () => { 'test token user B', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -244,7 +244,7 @@ describe('GraphQL API Core @core-api', () => { 'fail token user del', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -272,7 +272,7 @@ describe('GraphQL API Core @core-api', () => { 'test token user del', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -1694,7 +1694,7 @@ describe('GraphQL API Core @core-api', () => { 'this will be archived', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 4ebb659ff..0144d27fe 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -101,7 +101,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userA.id = await createUser(userA) const token = await createPersonalAccessToken(userA.id, 'test token user A', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -117,7 +117,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { 'test token user B', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -131,7 +131,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userC.token = `Bearer ${await createPersonalAccessToken( userC.id, 'test token user B', - [Scopes.Streams.Read, 'streams:write', 'users:read', 'users:email'] + [Scopes.Streams.Read, Scopes.Streams.Write, 'users:read', 'users:email'] )}` }) diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index 916dcf5ef..db53eab4e 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -42,7 +42,7 @@ describe('Upload/Download Routes @api-rest', () => { 'test token user A', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', @@ -58,7 +58,7 @@ describe('Upload/Download Routes @api-rest', () => { 'test token user B', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 923ad0781..45780661d 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -370,7 +370,7 @@ describe('Actors & Tokens @user-services', () => { before(async () => { pregeneratedToken = await createPersonalAccessToken(myTestActor.id, 'Whabadub', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'profile:read', 'users:email' ]) @@ -386,7 +386,7 @@ describe('Actors & Tokens @user-services', () => { }) it('Should create a personal api token', async () => { - const scopes = ['streams:write', 'profile:read'] + const scopes = [Scopes.Streams.Write, 'profile:read'] const name = 'My Test Token' myFirstToken = await createPersonalAccessToken(myTestActor.id, name, scopes) diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index d160073a2..6daa26dac 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -140,12 +140,12 @@ describe('Webhooks @webhooks', () => { userOne.token = `Bearer ${await createPersonalAccessToken( userOne.id, 'userOne test token', - [Scopes.Streams.Read, 'streams:write'] + [Scopes.Streams.Read, Scopes.Streams.Write] )}` userTwo.token = `Bearer ${await createPersonalAccessToken( userTwo.id, 'userTwo test token', - [Scopes.Streams.Read, 'streams:write'] + [Scopes.Streams.Read, Scopes.Streams.Write] )}` await grantPermissionsStream({ streamId: streamTwo.id, diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index fce314e56..3bea09459 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -32,7 +32,7 @@ const main = async () => { 'test token user A', [ Scopes.Streams.Read, - 'streams:write', + Scopes.Streams.Write, 'users:read', 'users:email', 'tokens:write', From 4d9078a6d78c9cb0b1e0728b66c5dd6d8e7bf7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:15:09 +0200 Subject: [PATCH 05/43] refactor(server scopes): use constants for users read scopes --- .../modules/activitystream/tests/activity.spec.js | 2 +- .../server/modules/core/graph/resolvers/users.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 6 +++--- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 972a18c11..3fd5d2719 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -81,7 +81,7 @@ describe('Activity @activity', () => { const normalScopesList = [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 2a26a808c..6a31e8398 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -66,7 +66,7 @@ module.exports = { async userSearch(parent, args, context) { await validateServerRole(context, Roles.Server.User) await validateScopes(context.scopes, 'profile:read') - await validateScopes(context.scopes, 'users:read') + await validateScopes(context.scopes, Scopes.Users.Read) if (args.query.length < 3) throw new UserInputError('Search query must be at least 3 carachters.') diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 02dd9cd49..b113d0408 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -47,7 +47,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Server.Setup, Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -62,7 +62,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -77,7 +77,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -245,7 +245,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -273,7 +273,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -1695,7 +1695,7 @@ describe('GraphQL API Core @core-api', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 0144d27fe..a5b9d3b89 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -102,7 +102,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { const token = await createPersonalAccessToken(userA.id, 'test token user A', [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -118,7 +118,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -131,7 +131,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userC.token = `Bearer ${await createPersonalAccessToken( userC.id, 'test token user B', - [Scopes.Streams.Read, Scopes.Streams.Write, 'users:read', 'users:email'] + [Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, 'users:email'] )}` }) diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index db53eab4e..e8d7db42c 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -43,7 +43,7 @@ describe('Upload/Download Routes @api-rest', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', @@ -59,7 +59,7 @@ describe('Upload/Download Routes @api-rest', () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 3bea09459..6149dc1e7 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -33,7 +33,7 @@ const main = async () => { [ Scopes.Streams.Read, Scopes.Streams.Write, - 'users:read', + Scopes.Users.Read, 'users:email', 'tokens:write', 'tokens:read', From d12cb9d34a583bd426ae778066c739b15fa8e874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:25:09 +0200 Subject: [PATCH 06/43] refactor(server scopes): use constants for users email scopes --- .../modules/activitystream/tests/activity.spec.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 6 +++--- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/modules/core/tests/users.spec.js | 2 +- packages/server/scripts/streamObjects.js | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 3fd5d2719..4172ffc67 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -82,7 +82,7 @@ describe('Activity @activity', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index b113d0408..63fa05044 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -48,7 +48,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -63,7 +63,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -78,7 +78,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -246,7 +246,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -274,7 +274,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -1696,7 +1696,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index a5b9d3b89..6f2b63140 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -103,7 +103,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -119,7 +119,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -131,7 +131,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { userC.token = `Bearer ${await createPersonalAccessToken( userC.id, 'test token user B', - [Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, 'users:email'] + [Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email] )}` }) diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index e8d7db42c..7f661460a 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -44,7 +44,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', @@ -60,7 +60,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 45780661d..80a43a6ba 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -372,7 +372,7 @@ describe('Actors & Tokens @user-services', () => { Scopes.Streams.Read, Scopes.Streams.Write, 'profile:read', - 'users:email' + Scopes.Users.Email ]) revokedToken = await createPersonalAccessToken(myTestActor.id, 'Mr. Revoked', [ Scopes.Streams.Read diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 6149dc1e7..241d59f4c 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -34,7 +34,7 @@ const main = async () => { Scopes.Streams.Read, Scopes.Streams.Write, Scopes.Users.Read, - 'users:email', + Scopes.Users.Email, 'tokens:write', 'tokens:read', 'profile:read', From 9b09a2d1c8a23b5e520535ad73cac6f51fc15ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:26:49 +0200 Subject: [PATCH 07/43] refactor(server scopes): use constants for tokens write scopes --- .../modules/activitystream/tests/activity.spec.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 4 ++-- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 4172ffc67..9910af6c5 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -83,7 +83,7 @@ describe('Activity @activity', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 63fa05044..6241ce7e2 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -49,7 +49,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -64,7 +64,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -79,7 +79,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -247,7 +247,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -275,7 +275,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email', @@ -1697,7 +1697,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 6f2b63140..5dd2ac112 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -104,7 +104,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -120,7 +120,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index 7f661460a..1f0f2d750 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -45,7 +45,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' @@ -61,7 +61,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 241d59f4c..9df4d8899 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -35,7 +35,7 @@ const main = async () => { Scopes.Streams.Write, Scopes.Users.Read, Scopes.Users.Email, - 'tokens:write', + Scopes.Tokens.Write, 'tokens:read', 'profile:read', 'profile:email' From f1e64e3b83b8e1fed4fc487422ba2baa3b6bb699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:27:33 +0200 Subject: [PATCH 08/43] refactor(server scopes): use constants for tokens read scopes --- .../modules/activitystream/tests/activity.spec.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 4 ++-- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 9910af6c5..9d8c9bf56 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -84,7 +84,7 @@ describe('Activity @activity', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 6241ce7e2..44ab6c04c 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -50,7 +50,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] @@ -65,7 +65,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] @@ -80,7 +80,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] @@ -248,7 +248,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] @@ -276,7 +276,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email', 'profile:delete' @@ -1698,7 +1698,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email', 'apps:read', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 5dd2ac112..31aa3a744 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -105,7 +105,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ]) @@ -121,7 +121,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index 1f0f2d750..144b64bd2 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -46,7 +46,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] @@ -62,7 +62,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 9df4d8899..4a2ec014c 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -36,7 +36,7 @@ const main = async () => { Scopes.Users.Read, Scopes.Users.Email, Scopes.Tokens.Write, - 'tokens:read', + Scopes.Tokens.Read, 'profile:read', 'profile:email' ] From 61c486909196e8b53263117b015b488b1f22d0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:29:54 +0200 Subject: [PATCH 09/43] refactor(server scopes): use constants for profile read scopes --- .../modules/activitystream/tests/activity.spec.js | 2 +- .../server/modules/auth/tests/appsGrapql.spec.js | 4 ++-- .../server/modules/core/graph/resolvers/users.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 4 ++-- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/modules/core/tests/users.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 9d8c9bf56..68b2de283 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -85,7 +85,7 @@ describe('Activity @activity', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] diff --git a/packages/server/modules/auth/tests/appsGrapql.spec.js b/packages/server/modules/auth/tests/appsGrapql.spec.js index 76f307119..7fd3c0dd2 100644 --- a/packages/server/modules/auth/tests/appsGrapql.spec.js +++ b/packages/server/modules/auth/tests/appsGrapql.spec.js @@ -34,7 +34,7 @@ describe('GraphQL @apps-api', () => { testUser.id = await createUser(testUser) testToken = `Bearer ${await createPersonalAccessToken(testUser.id, 'test token', [ - 'profile:read', + Scopes.Profile.Read, 'apps:read', 'apps:write' ])}` @@ -47,7 +47,7 @@ describe('GraphQL @apps-api', () => { testUser2.id = await createUser(testUser2) testToken2 = `Bearer ${await createPersonalAccessToken(testUser2.id, 'test token', [ - 'profile:read', + Scopes.Profile.Read, 'apps:read', 'apps:write' ])}` diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 6a31e8398..652c1c941 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -65,7 +65,7 @@ module.exports = { async userSearch(parent, args, context) { await validateServerRole(context, Roles.Server.User) - await validateScopes(context.scopes, 'profile:read') + await validateScopes(context.scopes, Scopes.Profile.Read) await validateScopes(context.scopes, Scopes.Users.Read) if (args.query.length < 3) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 44ab6c04c..8c11b055e 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -51,7 +51,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` @@ -66,7 +66,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` @@ -81,7 +81,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` @@ -249,7 +249,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` @@ -277,7 +277,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email', 'profile:delete' ] @@ -1699,7 +1699,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email', 'apps:read', 'apps:write', diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 31aa3a744..853bcb93c 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -106,7 +106,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ]) userA.token = `Bearer ${token}` @@ -122,7 +122,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index 144b64bd2..cdb0824ee 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -47,7 +47,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` @@ -63,7 +63,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 80a43a6ba..9d3b96377 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -371,7 +371,7 @@ describe('Actors & Tokens @user-services', () => { pregeneratedToken = await createPersonalAccessToken(myTestActor.id, 'Whabadub', [ Scopes.Streams.Read, Scopes.Streams.Write, - 'profile:read', + Scopes.Profile.Read, Scopes.Users.Email ]) revokedToken = await createPersonalAccessToken(myTestActor.id, 'Mr. Revoked', [ @@ -386,7 +386,7 @@ describe('Actors & Tokens @user-services', () => { }) it('Should create a personal api token', async () => { - const scopes = [Scopes.Streams.Write, 'profile:read'] + const scopes = [Scopes.Streams.Write, Scopes.Profile.Read] const name = 'My Test Token' myFirstToken = await createPersonalAccessToken(myTestActor.id, name, scopes) diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 4a2ec014c..9eeb9f4f4 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -37,7 +37,7 @@ const main = async () => { Scopes.Users.Email, Scopes.Tokens.Write, Scopes.Tokens.Read, - 'profile:read', + Scopes.Profile.Read, 'profile:email' ] )}` From c22e0dfe2354eac9ee169379bbd98ee7041ecc8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:32:08 +0200 Subject: [PATCH 10/43] refactor(server scopes): use constants for profile email scope --- .../modules/activitystream/tests/activity.spec.js | 2 +- .../server/modules/core/graph/resolvers/users.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 12 ++++++------ packages/server/modules/core/tests/graphSubs.spec.js | 4 ++-- packages/server/modules/core/tests/rest.spec.js | 4 ++-- packages/server/scripts/streamObjects.js | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activity.spec.js b/packages/server/modules/activitystream/tests/activity.spec.js index 68b2de283..ab360e266 100644 --- a/packages/server/modules/activitystream/tests/activity.spec.js +++ b/packages/server/modules/activitystream/tests/activity.spec.js @@ -86,7 +86,7 @@ describe('Activity @activity', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] // create users diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 652c1c941..53f285aac 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -97,7 +97,7 @@ module.exports = { // NOTE: we're redacting the field (returning null) rather than throwing a full error which would invalidate the request. if (context.userId === parent.id) { try { - await validateScopes(context.scopes, 'profile:email') + await validateScopes(context.scopes, Scopes.Profile.Email) return parent.email } catch (err) { return null diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 8c11b055e..dccbbd12f 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -52,7 +52,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` userB.id = await createUser(userB) @@ -67,7 +67,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` userC.id = await createUser(userC) @@ -82,7 +82,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` @@ -250,7 +250,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` @@ -278,7 +278,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email', + Scopes.Profile.Email, 'profile:delete' ] )}` @@ -1700,7 +1700,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email', + Scopes.Profile.Email, 'apps:read', 'apps:write', 'users:invite' diff --git a/packages/server/modules/core/tests/graphSubs.spec.js b/packages/server/modules/core/tests/graphSubs.spec.js index 853bcb93c..ee18e0d24 100644 --- a/packages/server/modules/core/tests/graphSubs.spec.js +++ b/packages/server/modules/core/tests/graphSubs.spec.js @@ -107,7 +107,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ]) userA.token = `Bearer ${token}` @@ -123,7 +123,7 @@ describe('GraphQL API Subscriptions @gql-subscriptions', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index cdb0824ee..50624dd13 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -48,7 +48,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` @@ -64,7 +64,7 @@ describe('Upload/Download Routes @api-rest', () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 9eeb9f4f4..5a4b1e254 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -38,7 +38,7 @@ const main = async () => { Scopes.Tokens.Write, Scopes.Tokens.Read, Scopes.Profile.Read, - 'profile:email' + Scopes.Profile.Email ] )}` From 143ec707a5354803758c231022c22a5387e2d540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:32:45 +0200 Subject: [PATCH 11/43] refactor(server scopes): use constants for profile delete scope --- packages/server/modules/core/graph/resolvers/users.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 53f285aac..631522358 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -165,7 +165,7 @@ module.exports = { // directives in the graphql schema itself. // Since I am paranoid, I'll leave them here too. await validateServerRole(context, Roles.Server.User) - await validateScopes(context.scopes, 'profile:delete') + await validateScopes(context.scopes, Scopes.Profile.Delete) await deleteUser(context.userId, args.user) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index dccbbd12f..6e13ba9f4 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -279,7 +279,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Read, Scopes.Profile.Read, Scopes.Profile.Email, - 'profile:delete' + Scopes.Profile.Delete ] )}` From 4a8fc084343de34f8c4443d9a8887367fa925a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:46:19 +0200 Subject: [PATCH 12/43] refactor(server scopes): use constants for apps read scope --- packages/server/modules/auth/scopes.js | 4 +++- packages/server/modules/auth/strategies/github.js | 3 +++ packages/server/modules/auth/tests/appsGrapql.spec.js | 4 ++-- packages/server/modules/core/tests/graph.spec.js | 2 +- packages/shared/src/core/constants.ts | 3 +++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/server/modules/auth/scopes.js b/packages/server/modules/auth/scopes.js index b84b895df..71a29f969 100644 --- a/packages/server/modules/auth/scopes.js +++ b/packages/server/modules/auth/scopes.js @@ -1,8 +1,10 @@ 'use strict' +const { Scopes } = require('@speckle/shared') + module.exports = [ { - name: 'apps:read', + name: Scopes.Apps.Read, description: 'See what applications you have created or have authorized.', public: false }, diff --git a/packages/server/modules/auth/strategies/github.js b/packages/server/modules/auth/strategies/github.js index 9f5b5efd8..7cffce873 100644 --- a/packages/server/modules/auth/strategies/github.js +++ b/packages/server/modules/auth/strategies/github.js @@ -30,6 +30,9 @@ module.exports = async (app, session, sessionStorage, finalizeAuth) => { clientID: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, callbackURL: new URL(strategy.callbackUrl, process.env.CANONICAL_URL).toString(), + // WARNING, the 'user:email' scope belongs to the GITHUB scopes + // DO NOT change it to our internal scope definitions !!! + // You have been warned. scope: ['profile', 'user:email'], passReqToCallback: true }, diff --git a/packages/server/modules/auth/tests/appsGrapql.spec.js b/packages/server/modules/auth/tests/appsGrapql.spec.js index 7fd3c0dd2..aad25d05a 100644 --- a/packages/server/modules/auth/tests/appsGrapql.spec.js +++ b/packages/server/modules/auth/tests/appsGrapql.spec.js @@ -35,7 +35,7 @@ describe('GraphQL @apps-api', () => { testUser.id = await createUser(testUser) testToken = `Bearer ${await createPersonalAccessToken(testUser.id, 'test token', [ Scopes.Profile.Read, - 'apps:read', + Scopes.Apps.Read, 'apps:write' ])}` @@ -48,7 +48,7 @@ describe('GraphQL @apps-api', () => { testUser2.id = await createUser(testUser2) testToken2 = `Bearer ${await createPersonalAccessToken(testUser2.id, 'test token', [ Scopes.Profile.Read, - 'apps:read', + Scopes.Apps.Read, 'apps:write' ])}` }) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 6e13ba9f4..8d9a503a2 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -1701,7 +1701,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Tokens.Read, Scopes.Profile.Read, Scopes.Profile.Email, - 'apps:read', + Scopes.Apps.Read, 'apps:write', 'users:invite' ] diff --git a/packages/shared/src/core/constants.ts b/packages/shared/src/core/constants.ts index 21e4c79a9..a224b9ab2 100644 --- a/packages/shared/src/core/constants.ts +++ b/packages/shared/src/core/constants.ts @@ -47,6 +47,9 @@ export const Scopes = Object.freeze({ Tokens: { Read: 'tokens:read', Write: 'tokens:write' + }, + Apps: { + Read: 'apps:read' } }) From 9e52fa934e330b4115123781f8569b9f53afb3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:48:20 +0200 Subject: [PATCH 13/43] refactor(server scopes): use constants for apps write scope --- packages/server/modules/auth/scopes.js | 2 +- packages/server/modules/auth/tests/appsGrapql.spec.js | 4 ++-- packages/server/modules/core/tests/graph.spec.js | 2 +- packages/shared/src/core/constants.ts | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/server/modules/auth/scopes.js b/packages/server/modules/auth/scopes.js index 71a29f969..0131b37de 100644 --- a/packages/server/modules/auth/scopes.js +++ b/packages/server/modules/auth/scopes.js @@ -9,7 +9,7 @@ module.exports = [ public: false }, { - name: 'apps:write', + name: Scopes.Apps.Write, description: 'Register applications on your behalf.', public: false } diff --git a/packages/server/modules/auth/tests/appsGrapql.spec.js b/packages/server/modules/auth/tests/appsGrapql.spec.js index aad25d05a..d5aeb163f 100644 --- a/packages/server/modules/auth/tests/appsGrapql.spec.js +++ b/packages/server/modules/auth/tests/appsGrapql.spec.js @@ -36,7 +36,7 @@ describe('GraphQL @apps-api', () => { testToken = `Bearer ${await createPersonalAccessToken(testUser.id, 'test token', [ Scopes.Profile.Read, Scopes.Apps.Read, - 'apps:write' + Scopes.Apps.Write ])}` testUser2 = { @@ -49,7 +49,7 @@ describe('GraphQL @apps-api', () => { testToken2 = `Bearer ${await createPersonalAccessToken(testUser2.id, 'test token', [ Scopes.Profile.Read, Scopes.Apps.Read, - 'apps:write' + Scopes.Apps.Write ])}` }) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 8d9a503a2..2dec2342e 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -1702,7 +1702,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Profile.Read, Scopes.Profile.Email, Scopes.Apps.Read, - 'apps:write', + Scopes.Apps.Write, 'users:invite' ] )}` diff --git a/packages/shared/src/core/constants.ts b/packages/shared/src/core/constants.ts index a224b9ab2..2da34ab3c 100644 --- a/packages/shared/src/core/constants.ts +++ b/packages/shared/src/core/constants.ts @@ -49,7 +49,8 @@ export const Scopes = Object.freeze({ Write: 'tokens:write' }, Apps: { - Read: 'apps:read' + Read: 'apps:read', + Write: 'apps:write' } }) From ab48fef46a67af87d6732f0f8bfb36c0fa11ab09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:49:30 +0200 Subject: [PATCH 14/43] refactor(server scopes): use constants for users invite scope --- packages/server/modules/core/tests/graph.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 2dec2342e..ee239d02e 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -1703,7 +1703,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Profile.Email, Scopes.Apps.Read, Scopes.Apps.Write, - 'users:invite' + Scopes.Users.Invite ] )}` await archiveUser({ userId: archivedUser.id }) From bd98b56e02531776da5fd2f8941cf5d9d9d0b7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 13:51:24 +0200 Subject: [PATCH 15/43] refactor(server scopes): use constants for tokens write scope --- packages/server/modules/auth/rest/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/modules/auth/rest/index.js b/packages/server/modules/auth/rest/index.js index a10b3cc4d..63c3f9057 100644 --- a/packages/server/modules/auth/rest/index.js +++ b/packages/server/modules/auth/rest/index.js @@ -14,6 +14,7 @@ const { revokeRefreshToken } = require(`@/modules/auth/services/apps`) const { validateScopes } = require(`@/modules/shared`) const { InvalidAccessCodeRequestError } = require('@/modules/auth/errors') const { ForbiddenError } = require('apollo-server-errors') +const { Scopes } = require('@speckle/shared') // TODO: Secure these endpoints! module.exports = (app) => { @@ -38,7 +39,7 @@ module.exports = (app) => { if (!valid) throw new InvalidAccessCodeRequestError('Invalid token') // 2. Validate token scopes - await validateScopes(scopes, 'tokens:write') + await validateScopes(scopes, Scopes.Tokens.Write) const ac = await createAuthorizationCode({ appId, userId, challenge }) return res.redirect(`${app.redirectUrl}?access_code=${ac}`) From a2bb36e43ec68008a0a4ffc9e5c6096297e4e6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 14:08:47 +0200 Subject: [PATCH 16/43] refactor(server scopes): use constants for streams owner role --- packages/server/modules/comments/services/index.js | 3 ++- packages/server/modules/core/graph/resolvers/streams.js | 8 ++++---- packages/server/modules/core/repositories/streams.ts | 2 +- packages/server/modules/core/services/users.js | 4 ++-- packages/server/modules/core/tests/generic.spec.js | 2 +- packages/server/modules/core/tests/graph.spec.js | 2 +- packages/server/modules/core/tests/users.spec.js | 4 ++-- .../server/modules/webhooks/graph/resolvers/webhooks.js | 9 +++++---- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/server/modules/comments/services/index.js b/packages/server/modules/comments/services/index.js index 1fc117ab7..93231162b 100644 --- a/packages/server/modules/comments/services/index.js +++ b/packages/server/modules/comments/services/index.js @@ -13,6 +13,7 @@ const { markCommentViewed } = require('@/modules/comments/repositories/comments') const { clamp } = require('lodash') +const { Roles } = require('@speckle/shared') const Comments = () => knex('comments') const CommentLinks = () => knex('comment_links') @@ -221,7 +222,7 @@ module.exports = { .first() if (comment.authorId !== userId) { - if (!aclEntry || aclEntry.role !== 'stream:owner') + if (!aclEntry || aclEntry.role !== Roles.Stream.Owner) throw new ForbiddenError("You don't have permission to archive the comment") } diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index 1261afff4..932884436 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -221,13 +221,13 @@ module.exports = { }, async streamUpdate(parent, args, context) { - await authorizeResolver(context.userId, args.stream.id, 'stream:owner') + await authorizeResolver(context.userId, args.stream.id, Roles.Stream.Owner) await updateStreamAndNotify(args.stream, context.userId) return true }, async streamDelete(parent, args, context, info) { - await authorizeResolver(context.userId, args.id, 'stream:owner') + await authorizeResolver(context.userId, args.id, Roles.Stream.Owner) return await _deleteStream(parent, args, context, info) }, @@ -246,7 +246,7 @@ module.exports = { await authorizeResolver( context.userId, args.permissionParams.streamId, - 'stream:owner' + Roles.Stream.Owner ) const result = await updateStreamRoleAndNotify( @@ -260,7 +260,7 @@ module.exports = { await authorizeResolver( context.userId, args.permissionParams.streamId, - 'stream:owner' + Roles.Stream.Owner ) const result = await updateStreamRoleAndNotify( diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index 482ef00b8..458a3d478 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -930,7 +930,7 @@ export async function revokeStreamPermissions(params: { .select('*') .first() - if (aclEntry?.role === 'stream:owner') { + if (aclEntry?.role === Roles.Stream.Owner) { const [countObj] = await StreamAcl.knex() .where({ resourceId: streamId, diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index f6f7e5efe..f96c68c2e 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -227,9 +227,9 @@ module.exports = { ( -- Get streams ids on which the user is owner SELECT "resourceId" FROM stream_acl - WHERE role = 'stream:owner' AND "userId" = ? + WHERE role = ${Roles.Stream.Owner} AND "userId" = ? ) AS us ON acl."resourceId" = us."resourceId" - WHERE acl.role = 'stream:owner' + WHERE acl.role = ${Roles.Stream.Owner} GROUP BY (acl."resourceId") ) AS soc WHERE cnt = 1 diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index 2c4bb95f6..75f2a246e 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -154,7 +154,7 @@ describe('Generic AuthN & AuthZ controller tests', () => { myStream.id, 'stream:contributor' ) - expect(role).to.equal('stream:owner') + expect(role).to.equal(Roles.Stream.Owner) }) it('should get the passed in role for server:admins if override enabled', async () => { diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index ee239d02e..d7adb25fa 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -1277,7 +1277,7 @@ describe('GraphQL API Core @core-api', () => { expect(stream.name).to.equal('TS1 (u A) Private UPDATED') expect(stream.collaborators).to.have.lengthOf(2) expect(stream.collaborators[0].role).to.equal('stream:contributor') - expect(stream.collaborators[1].role).to.equal('stream:owner') + expect(stream.collaborators[1].role).to.equal(Roles.Stream.Owner) }) it('Should retrieve a public stream even if not authenticated', async () => { diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 9d3b96377..e49fad519 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -41,7 +41,7 @@ const { const { createObject } = require('../services/objects') const { beforeEachContext } = require('@/test/hooks') -const { Scopes } = require('@speckle/shared') +const { Scopes, Roles } = require('@speckle/shared') describe('Actors & Tokens @user-services', () => { const myTestActor = { @@ -194,7 +194,7 @@ describe('Actors & Tokens @user-services', () => { await grantPermissionsStream({ streamId: multiOwnerStream.id, userId: myTestActor.id, - role: 'stream:owner' + role: Roles.Stream.Owner }) // create a branch for ballmer on the multiowner stream diff --git a/packages/server/modules/webhooks/graph/resolvers/webhooks.js b/packages/server/modules/webhooks/graph/resolvers/webhooks.js index 90186ac36..faaee85bd 100644 --- a/packages/server/modules/webhooks/graph/resolvers/webhooks.js +++ b/packages/server/modules/webhooks/graph/resolvers/webhooks.js @@ -10,11 +10,12 @@ const { getLastWebhookEvents, getWebhookEventsCount } = require('../../services/webhooks') +const { Roles } = require('@speckle/shared') module.exports = { Stream: { async webhooks(parent, args, context) { - await authorizeResolver(context.userId, parent.id, 'stream:owner') + await authorizeResolver(context.userId, parent.id, Roles.Stream.Owner) if (args.id) { const wh = await getWebhook({ id: args.id }) @@ -41,7 +42,7 @@ module.exports = { Mutation: { async webhookCreate(parent, args, context) { - await authorizeResolver(context.userId, args.webhook.streamId, 'stream:owner') + await authorizeResolver(context.userId, args.webhook.streamId, Roles.Stream.Owner) const id = await createWebhook({ streamId: args.webhook.streamId, @@ -55,7 +56,7 @@ module.exports = { return id }, async webhookUpdate(parent, args, context) { - await authorizeResolver(context.userId, args.webhook.streamId, 'stream:owner') + await authorizeResolver(context.userId, args.webhook.streamId, Roles.Stream.Owner) const wh = await getWebhook({ id: args.webhook.id }) if (args.webhook.streamId !== wh.streamId) @@ -75,7 +76,7 @@ module.exports = { return !!updated }, async webhookDelete(parent, args, context) { - await authorizeResolver(context.userId, args.webhook.streamId, 'stream:owner') + await authorizeResolver(context.userId, args.webhook.streamId, Roles.Stream.Owner) const wh = await getWebhook({ id: args.webhook.id }) if (args.webhook.streamId !== wh.streamId) From 23989174d3aa951cbe8993db960625c2a05599b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 14:12:41 +0200 Subject: [PATCH 17/43] refactor(server scopes): use constants for streams contributor role --- .../modules/core/graph/resolvers/branches.js | 7 ++++--- .../modules/core/graph/resolvers/commits.js | 7 ++++--- .../modules/core/graph/resolvers/objects.js | 2 +- .../server/modules/core/rest/authUtils.js | 2 +- .../server/modules/core/tests/generic.spec.js | 20 +++++++++++-------- .../server/modules/core/tests/graph.spec.js | 2 +- .../server/modules/core/tests/streams.spec.ts | 2 +- .../modules/webhooks/tests/webhooks.spec.js | 4 ++-- 8 files changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/branches.js b/packages/server/modules/core/graph/resolvers/branches.js index 4bf2b04b8..e22d7c968 100644 --- a/packages/server/modules/core/graph/resolvers/branches.js +++ b/packages/server/modules/core/graph/resolvers/branches.js @@ -15,6 +15,7 @@ const { } = require('@/modules/core/services/branch/retrieval') const { getUserById } = require('../../services/users') +const { Roles } = require('@speckle/shared') // subscription events const BRANCH_CREATED = BranchPubsubEvents.BranchCreated @@ -62,7 +63,7 @@ module.exports = { await authorizeResolver( context.userId, args.branch.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const { id } = await createBranchAndNotify(args.branch, context.userId) @@ -74,7 +75,7 @@ module.exports = { await authorizeResolver( context.userId, args.branch.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const newBranch = await updateBranchAndNotify(args.branch, context.userId) @@ -85,7 +86,7 @@ module.exports = { await authorizeResolver( context.userId, args.branch.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const deleted = await deleteBranchAndNotify(args.branch, context.userId) diff --git a/packages/server/modules/core/graph/resolvers/commits.js b/packages/server/modules/core/graph/resolvers/commits.js index d31ad2bdb..802490c50 100644 --- a/packages/server/modules/core/graph/resolvers/commits.js +++ b/packages/server/modules/core/graph/resolvers/commits.js @@ -39,6 +39,7 @@ const { validateStreamAccess } = require('@/modules/core/services/streams/streamAccessService') const { StreamInvalidAccessError } = require('@/modules/core/errors/stream') +const { Roles } = require('@speckle/shared') // subscription events const COMMIT_CREATED = CommitPubsubEvents.CommitCreated @@ -167,7 +168,7 @@ module.exports = { await authorizeResolver( context.userId, args.commit.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const rateLimitResult = await getRateLimitResult( @@ -190,7 +191,7 @@ module.exports = { await authorizeResolver( context.userId, args.commit.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) await updateCommitAndNotify(args.commit, context.userId) @@ -218,7 +219,7 @@ module.exports = { await authorizeResolver( context.userId, args.commit.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const deleted = await deleteCommitAndNotify( diff --git a/packages/server/modules/core/graph/resolvers/objects.js b/packages/server/modules/core/graph/resolvers/objects.js index faeb878bd..0b9eb0e5c 100644 --- a/packages/server/modules/core/graph/resolvers/objects.js +++ b/packages/server/modules/core/graph/resolvers/objects.js @@ -65,7 +65,7 @@ module.exports = { await authorizeResolver( context.userId, args.objectInput.streamId, - 'stream:contributor' + Roles.Stream.Contributor ) const ids = await createObjects( diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index b7c95e24d..4d075efe1 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -59,7 +59,7 @@ module.exports = { } try { - await authorizeResolver(req.context.userId, streamId, 'stream:contributor') + await authorizeResolver(req.context.userId, streamId, Roles.Stream.Contributor) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index 75f2a246e..39160f278 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -152,7 +152,7 @@ describe('Generic AuthN & AuthZ controller tests', () => { const role = await authorizeResolver( serverOwner.id, myStream.id, - 'stream:contributor' + Roles.Stream.Contributor ) expect(role).to.equal(Roles.Stream.Owner) }) @@ -163,13 +163,17 @@ describe('Generic AuthN & AuthZ controller tests', () => { const role = await authorizeResolver( serverOwner.id, myStream.id, - 'stream:contributor' + Roles.Stream.Contributor ) - expect(role).to.equal('stream:contributor') + expect(role).to.equal(Roles.Stream.Contributor) }) it('should not allow server:admins to be anything if adminOverride is disabled', async () => { try { - await authorizeResolver(serverOwner.id, notMyStream.id, 'stream:contributor') + await authorizeResolver( + serverOwner.id, + notMyStream.id, + Roles.Stream.Contributor + ) throw 'This should have thrown' } catch (e) { expect(e instanceof ForbiddenError) @@ -183,14 +187,14 @@ describe('Generic AuthN & AuthZ controller tests', () => { const role = await authorizeResolver( serverOwner.id, notMyStream.id, - 'stream:contributor' + Roles.Stream.Contributor ) - expect(role).to.equal('stream:contributor') + expect(role).to.equal(Roles.Stream.Contributor) }) it('should not allow server:users to be anything if adminOverride is disabled', async () => { try { - await authorizeResolver(otherGuy.id, myStream.id, 'stream:contributor') + await authorizeResolver(otherGuy.id, myStream.id, Roles.Stream.Contributor) throw 'This should have thrown' } catch (e) { expect(e instanceof ForbiddenError) @@ -201,7 +205,7 @@ describe('Generic AuthN & AuthZ controller tests', () => { envHelperMock.enable() envHelperMock.mockFunction('adminOverrideEnabled', () => true) try { - await authorizeResolver(otherGuy.id, myStream.id, 'stream:contributor') + await authorizeResolver(otherGuy.id, myStream.id, Roles.Stream.Contributor) throw 'This should have thrown' } catch (e) { expect(e instanceof ForbiddenError) diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index d7adb25fa..90c0b5a0a 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -1276,7 +1276,7 @@ describe('GraphQL API Core @core-api', () => { expect(stream.name).to.equal('TS1 (u A) Private UPDATED') expect(stream.collaborators).to.have.lengthOf(2) - expect(stream.collaborators[0].role).to.equal('stream:contributor') + expect(stream.collaborators[0].role).to.equal(Roles.Stream.Contributor) expect(stream.collaborators[1].role).to.equal(Roles.Stream.Owner) }) diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index fdc202cc5..19d6c5bd6 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -253,7 +253,7 @@ describe('Streams @core-streams', () => { await grantPermissionsStream({ streamId: updatableStream.id, userId: userTwo.id, - role: 'stream:contributor' + role: Roles.Stream.Contributor }) // await sleep(100) diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index 6daa26dac..be5904a00 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -16,7 +16,7 @@ const { } = require('../services/webhooks') const { createUser } = require('../../core/services/users') const { createStream, grantPermissionsStream } = require('../../core/services/streams') -const { Scopes } = require('@speckle/shared') +const { Scopes, Roles } = require('@speckle/shared') describe('Webhooks @webhooks', () => { let server, sendRequest, app @@ -150,7 +150,7 @@ describe('Webhooks @webhooks', () => { await grantPermissionsStream({ streamId: streamTwo.id, userId: userOne.id, - role: 'stream:contributor' + role: Roles.Stream.Contributor }) }) From 8f6ec4a025654f39607353ab495c305a6ec7ddc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 14:14:17 +0200 Subject: [PATCH 18/43] refactor(server scopes): use constants for stream reviewer role --- .../modules/core/graph/resolvers/branches.js | 18 +++++++++++--- .../modules/core/graph/resolvers/commits.js | 24 +++++++++++++++---- .../modules/core/graph/resolvers/streams.js | 10 +++++--- .../server/modules/core/rest/authUtils.js | 2 +- packages/server/modules/previews/index.js | 4 ++-- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/branches.js b/packages/server/modules/core/graph/resolvers/branches.js index e22d7c968..c1be66e98 100644 --- a/packages/server/modules/core/graph/resolvers/branches.js +++ b/packages/server/modules/core/graph/resolvers/branches.js @@ -98,7 +98,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([BRANCH_CREATED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) return payload.streamId === variables.streamId } @@ -109,7 +113,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([BRANCH_UPDATED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) const streamMatch = payload.streamId === variables.streamId if (streamMatch && variables.branchId) { @@ -125,7 +133,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([BRANCH_DELETED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) return payload.streamId === variables.streamId } diff --git a/packages/server/modules/core/graph/resolvers/commits.js b/packages/server/modules/core/graph/resolvers/commits.js index 802490c50..4a20886cd 100644 --- a/packages/server/modules/core/graph/resolvers/commits.js +++ b/packages/server/modules/core/graph/resolvers/commits.js @@ -199,7 +199,11 @@ module.exports = { }, async commitReceive(parent, args, context) { - await authorizeResolver(context.userId, args.input.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + args.input.streamId, + Roles.Stream.Reviewer + ) const commit = await getCommitById({ streamId: args.input.streamId, @@ -245,7 +249,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([COMMIT_CREATED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) return payload.streamId === variables.streamId } ) @@ -255,7 +263,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([COMMIT_UPDATED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) const streamMatch = payload.streamId === variables.streamId if (streamMatch && variables.commitId) { @@ -271,7 +283,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([COMMIT_DELETED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) return payload.streamId === variables.streamId } diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index 932884436..941ba633d 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -85,7 +85,7 @@ module.exports = { throw new StreamNotFoundError('Stream not found') } - await authorizeResolver(context.userId, args.id, 'stream:reviewer') + await authorizeResolver(context.userId, args.id, Roles.Stream.Reviewer) if (!stream.isPublic) { await validateServerRole(context, Roles.Server.User) @@ -310,7 +310,7 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([STREAM_UPDATED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.id, 'stream:reviewer') + await authorizeResolver(context.userId, payload.id, Roles.Stream.Reviewer) return payload.id === variables.streamId } ) @@ -320,7 +320,11 @@ module.exports = { subscribe: withFilter( () => pubsub.asyncIterator([STREAM_DELETED]), async (payload, variables, context) => { - await authorizeResolver(context.userId, payload.streamId, 'stream:reviewer') + await authorizeResolver( + context.userId, + payload.streamId, + Roles.Stream.Reviewer + ) return payload.streamId === variables.streamId } ) diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index 4d075efe1..3327b73a9 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -33,7 +33,7 @@ module.exports = { } try { - await authorizeResolver(req.context.userId, streamId, 'stream:reviewer') + await authorizeResolver(req.context.userId, streamId, Roles.Stream.Reviewer) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/previews/index.js b/packages/server/modules/previews/index.js index 22e6319b6..9ebba9b45 100644 --- a/packages/server/modules/previews/index.js +++ b/packages/server/modules/previews/index.js @@ -21,7 +21,7 @@ const { moduleLogger, logger } = require('@/logging/logging') const { listenForPreviewGenerationUpdates } = require('@/modules/previews/services/resultListener') -const { Scopes } = require('@speckle/shared') +const { Scopes, Roles } = require('@speckle/shared') const httpErrorImage = (httpErrorCode) => require.resolve(`#/assets/previews/images/preview_${httpErrorCode}.png`) @@ -154,7 +154,7 @@ exports.init = (app) => { await authorizeResolver( req.context.userId, req.params.streamId, - 'stream:reviewer' + Roles.Stream.Reviewer ) } catch (err) { return { hasPermissions: false, httpErrorCode: 401 } From 5942c0967130dee11345ecfb22b85864de71e68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 21:00:51 +0200 Subject: [PATCH 19/43] feat(server, helm): add guest role feature flag --- packages/server/modules/shared/helpers/envHelper.ts | 4 ++++ utils/helm/speckle-server/templates/server/deployment.yml | 5 +++++ utils/helm/speckle-server/values.yaml | 3 +++ 3 files changed, 12 insertions(+) diff --git a/packages/server/modules/shared/helpers/envHelper.ts b/packages/server/modules/shared/helpers/envHelper.ts index 3fc154e28..91afa4199 100644 --- a/packages/server/modules/shared/helpers/envHelper.ts +++ b/packages/server/modules/shared/helpers/envHelper.ts @@ -187,3 +187,7 @@ export function speckleAutomateUrl() { export function ignoreMissingMigrations() { return ['1', 'true'].includes(process.env.IGNORE_MISSING_MIRATIONS || 'false') } + +export function enableServerGuests() { + return ['1', 'true'].includes(process.env.ENABLE_SERVER_GUESTS || 'false') +} diff --git a/utils/helm/speckle-server/templates/server/deployment.yml b/utils/helm/speckle-server/templates/server/deployment.yml index 9d7bd9621..8f70b96c6 100644 --- a/utils/helm/speckle-server/templates/server/deployment.yml +++ b/utils/helm/speckle-server/templates/server/deployment.yml @@ -209,6 +209,11 @@ spec: key: {{ default "google_client_secret" .Values.server.auth.google.clientSecret.secretKey }} {{- end }} + {{- if .Values.serverGuestsEnabled }} + - name: ENABLE_SERVER_GUESTS + value: true + {{- end }} + # Github Auth {{- if .Values.server.auth.github.enabled }} - name: STRATEGY_GITHUB diff --git a/utils/helm/speckle-server/values.yaml b/utils/helm/speckle-server/values.yaml index d9fabb523..c9e0c9956 100644 --- a/utils/helm/speckle-server/values.yaml +++ b/utils/helm/speckle-server/values.yaml @@ -97,6 +97,9 @@ file_size_limit_mb: 100 ## enable_prometheus_monitoring: false +## @param serverGuestsEnabled Enables users to join the server as Guests, having limiter permissions +serverGuestsEnabled: false + prometheusMonitoring: ## @param prometheusMonitoring.namespace If provided, deploys Speckle's Prometheus resources (e.g. ServiceMonitor) in the given namespace ## Prometheus prior to v0.19.0, or any version when deployed with default parameters, expects ServiceMonitors to be deployed within the same namespace. From 879b4af25bd927475a8e310997ac3a70af673a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 21:19:36 +0200 Subject: [PATCH 20/43] ci(circleci): update server tests resource class, fixing oom --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5f99e630f..68b877d0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -333,6 +333,7 @@ jobs: command: server /data --console-address ":9001" # environment: + resource_class: medium+ environment: NODE_ENV: test DATABASE_URL: 'postgres://speckle:speckle@127.0.0.1:5432/speckle2_test' From 606a04adf8f071e2fbab4da4ed878250b65a3ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 21:33:49 +0200 Subject: [PATCH 21/43] chore(helm): update values json schema --- utils/helm/speckle-server/values.schema.json | 5 +++++ utils/helm/speckle-server/values.yaml | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/utils/helm/speckle-server/values.schema.json b/utils/helm/speckle-server/values.schema.json index 850a6e2ba..b9491a413 100644 --- a/utils/helm/speckle-server/values.schema.json +++ b/utils/helm/speckle-server/values.schema.json @@ -97,6 +97,11 @@ "description": "This maximum size of any single file (unit is Megabytes) that can be uploaded to Speckle", "default": 100 }, + "serverGuestsEnabled": { + "type": "boolean", + "description": "Enables users to join the server as Guests, having limiter permissions", + "default": false + }, "enable_prometheus_monitoring": { "type": "boolean", "description": "If enabled, Speckle deploys a Prometheus ServiceMonitor resource", diff --git a/utils/helm/speckle-server/values.yaml b/utils/helm/speckle-server/values.yaml index c9e0c9956..079a3e70f 100644 --- a/utils/helm/speckle-server/values.yaml +++ b/utils/helm/speckle-server/values.yaml @@ -84,6 +84,9 @@ secretName: server-vars ## file_size_limit_mb: 100 +## @param serverGuestsEnabled Enables users to join the server as Guests, having limiter permissions +serverGuestsEnabled: false + ## @section Monitoring ## @descriptionStart ## This enables metrics generated by Speckle to be ingested by Prometheus: https://prometheus.io/ @@ -97,9 +100,6 @@ file_size_limit_mb: 100 ## enable_prometheus_monitoring: false -## @param serverGuestsEnabled Enables users to join the server as Guests, having limiter permissions -serverGuestsEnabled: false - prometheusMonitoring: ## @param prometheusMonitoring.namespace If provided, deploys Speckle's Prometheus resources (e.g. ServiceMonitor) in the given namespace ## Prometheus prior to v0.19.0, or any version when deployed with default parameters, expects ServiceMonitors to be deployed within the same namespace. From d8b4c1c0f9c8c7e37be0da91ee97cb69911df828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 21:52:50 +0200 Subject: [PATCH 22/43] feat(shared): add Roles.Server.Guest constan --- packages/shared/src/core/constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/src/core/constants.ts b/packages/shared/src/core/constants.ts index 2da34ab3c..ec45aeaa8 100644 --- a/packages/shared/src/core/constants.ts +++ b/packages/shared/src/core/constants.ts @@ -14,6 +14,7 @@ export const Roles = Object.freeze({ Server: { Admin: 'server:admin', User: 'server:user', + Guest: 'server:guest', ArchivedUser: 'server:archived-user' } }) From 399a088cfb715d47c9669dbb3c3d280166274c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 22:00:39 +0200 Subject: [PATCH 23/43] feat(server): add server role definition --- packages/server/modules/core/roles.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/server/modules/core/roles.js b/packages/server/modules/core/roles.js index e73421ccf..d4e72a937 100644 --- a/packages/server/modules/core/roles.js +++ b/packages/server/modules/core/roles.js @@ -27,6 +27,18 @@ module.exports = [ weight: 100, public: false }, + // TODO: should this be dynamically pushed if guest role is enabled? + // feels risky, since feature can be toggled on and off, + // but user roles are not updated + // can leave the guest users in a broken state + { + name: Roles.Server.Guest, + description: 'Has limited access to the server.', + resourceTarget: 'server', + aclTableName: 'server_acl', + weight: 50, + public: false + }, { name: Roles.Server.ArchivedUser, description: 'No longer has access to the server.', From 6d9a1eb20e017f7876868091a32f3f8db84ac961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 22:04:35 +0200 Subject: [PATCH 24/43] feat(server): add graphql guest role definition --- packages/server/modules/core/graph/directives/hasRole.js | 1 + packages/server/modules/core/graph/generated/graphql.ts | 1 + packages/server/modules/core/helpers/graphTypes.ts | 2 ++ packages/server/test/graphql/generated/graphql.ts | 1 + 4 files changed, 5 insertions(+) diff --git a/packages/server/modules/core/graph/directives/hasRole.js b/packages/server/modules/core/graph/directives/hasRole.js index 985d17f3f..681aae2c6 100644 --- a/packages/server/modules/core/graph/directives/hasRole.js +++ b/packages/server/modules/core/graph/directives/hasRole.js @@ -19,6 +19,7 @@ module.exports = { enum ServerRole { SERVER_USER SERVER_ADMIN + SERVER_GUEST SERVER_ARCHIVED_USER } diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index aad3bb312..e27170db0 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -1878,6 +1878,7 @@ export type ServerInviteCreateInput = { export enum ServerRole { ServerAdmin = 'SERVER_ADMIN', ServerArchivedUser = 'SERVER_ARCHIVED_USER', + ServerGuest = 'SERVER_GUEST', ServerUser = 'SERVER_USER' } diff --git a/packages/server/modules/core/helpers/graphTypes.ts b/packages/server/modules/core/helpers/graphTypes.ts index a3276569a..c82ed902f 100644 --- a/packages/server/modules/core/helpers/graphTypes.ts +++ b/packages/server/modules/core/helpers/graphTypes.ts @@ -121,5 +121,7 @@ export function mapServerRoleToValue(graphqlServerRole: ServerRole): ServerRoles return Roles.Server.Admin case ServerRole.ServerArchivedUser: return Roles.Server.ArchivedUser + case ServerRole.ServerGuest: + return Roles.Server.Guest } } diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 93ffb6d54..42d86872c 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -1869,6 +1869,7 @@ export type ServerInviteCreateInput = { export enum ServerRole { ServerAdmin = 'SERVER_ADMIN', ServerArchivedUser = 'SERVER_ARCHIVED_USER', + ServerGuest = 'SERVER_GUEST', ServerUser = 'SERVER_USER' } From 2d3369c79f55f01bc496511d908b6382c9699573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 26 Jul 2023 22:13:30 +0200 Subject: [PATCH 25/43] ci(circleci): bump resource class --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 68b877d0d..07d8f9cd9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -333,7 +333,7 @@ jobs: command: server /data --console-address ":9001" # environment: - resource_class: medium+ + resource_class: large environment: NODE_ENV: test DATABASE_URL: 'postgres://speckle:speckle@127.0.0.1:5432/speckle2_test' From a86cd75395af672aac57c2f3e0b5c23a17c0d6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 10:19:29 +0200 Subject: [PATCH 26/43] fix(server): fix delete user sql query --- packages/server/modules/core/services/users.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index f96c68c2e..ded009b0c 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -227,9 +227,9 @@ module.exports = { ( -- Get streams ids on which the user is owner SELECT "resourceId" FROM stream_acl - WHERE role = ${Roles.Stream.Owner} AND "userId" = ? + WHERE role = '${Roles.Stream.Owner}' AND "userId" = ? ) AS us ON acl."resourceId" = us."resourceId" - WHERE acl.role = ${Roles.Stream.Owner} + WHERE acl.role = '${Roles.Stream.Owner}' GROUP BY (acl."resourceId") ) AS soc WHERE cnt = 1 From 2fa8a21754b24064c0e24bc6eb3c6bdcc28af9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 13:30:32 +0200 Subject: [PATCH 27/43] feat(server): implement switch user role to guest --- .../modules/core/graph/resolvers/users.js | 25 +++-- .../server/modules/core/services/users.js | 27 ++---- .../server/modules/core/tests/graph.spec.js | 8 +- .../server/modules/core/tests/users.spec.js | 4 +- .../modules/core/tests/usersAdmin.spec.js | 91 +++++++++++++------ .../modules/shared/helpers/envHelper.ts | 2 +- 6 files changed, 91 insertions(+), 66 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 631522358..2824fd222 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -6,10 +6,8 @@ const { getUserRole, deleteUser, searchUsers, - makeUserAdmin, - unmakeUserAdmin, - archiveUser -} = require('../../services/users') + changeUserRole +} = require('@/modules/core/services/users') const { updateUserAndNotify } = require('@/modules/core/services/users/management') const { saveActivity } = require('@/modules/activitystream/services') const { ActionTypes } = require('@/modules/activitystream/helpers/types') @@ -21,6 +19,7 @@ const { const { Roles, Scopes } = require('@speckle/shared') const { markOnboardingComplete } = require('@/modules/core/repositories/users') const { UsersMeta } = require('@/modules/core/dbSchema') +const { guestServerRoleEnabled } = require('@/modules/shared/helpers/envHelper') /** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */ module.exports = { @@ -130,24 +129,22 @@ module.exports = { } }, Mutation: { - async userUpdate(parent, args, context) { + async userUpdate(_parent, args, context) { await validateServerRole(context, Roles.Server.User) await updateUserAndNotify(context.userId, args.user) return true }, - async userRoleChange(parent, args) { - const roleChangers = { - [Roles.Server.Admin]: makeUserAdmin, - [Roles.Server.User]: unmakeUserAdmin, - [Roles.Server.ArchivedUser]: archiveUser - } - const roleChanger = roleChangers[args.userRoleInput.role] - await roleChanger({ userId: args.userRoleInput.id }) + async userRoleChange(_parent, args) { + await changeUserRole({ + role: args.userRoleInput.role, + userId: args.userRoleInput.id, + guestRoleEnabled: guestServerRoleEnabled() + }) return true }, - async adminDeleteUser(parent, args, context) { + async adminDeleteUser(_parent, args, context) { await validateServerRole(context, Roles.Server.Admin) const user = await getUserByEmail({ email: args.userConfirmation.email }) await deleteUser(user.id) diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index ded009b0c..4eeae7bf3 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -28,7 +28,7 @@ const { } = require('@/modules/core/errors/userinput') const { Roles } = require('@speckle/shared') -const changeUserRole = async ({ userId, role }) => +const _changeUserRole = async ({ userId, role }) => await Acl().where({ userId }).update({ role }) const countAdminUsers = async () => { @@ -264,25 +264,18 @@ module.exports = { return users }, - async makeUserAdmin({ userId }) { - await changeUserRole({ userId, role: Roles.Server.Admin }) - }, - - async unmakeUserAdmin({ userId }) { - // dont delete last admin role - await _ensureAtleastOneAdminRemains(userId) - await changeUserRole({ userId, role: Roles.Server.User }) - }, - - async archiveUser({ userId }) { - // dont change last admin to archived - await _ensureAtleastOneAdminRemains(userId) - await changeUserRole({ userId, role: Roles.Server.ArchivedUser }) - }, - async countUsers(searchQuery = null) { const query = getUsersBaseQuery(searchQuery) const [userCount] = await query.count() return parseInt(userCount.count) + }, + + async changeUserRole({ userId, role, guestRoleEnabled = false }) { + if (!Object.values(Roles.Server).includes(role)) + throw new UserInputError(`Invalid role specified: ${role}`) + if (!guestRoleEnabled && role === Roles.Server.Guest) + throw new UserInputError('Guest role is not enabled') + if (role !== Roles.Server.Admin) await _ensureAtleastOneAdminRemains(userId) + await _changeUserRole({ userId, role }) } } diff --git a/packages/server/modules/core/tests/graph.spec.js b/packages/server/modules/core/tests/graph.spec.js index 90c0b5a0a..fb23c2e1e 100644 --- a/packages/server/modules/core/tests/graph.spec.js +++ b/packages/server/modules/core/tests/graph.spec.js @@ -5,7 +5,11 @@ const request = require('supertest') const { beforeEachContext, initializeTestServer } = require(`@/test/hooks`) const { generateManyObjects } = require(`@/test/helpers`) -const { createUser, getUsers, archiveUser } = require('../services/users') +const { + createUser, + getUsers, + changeUserRole +} = require('@/modules/core/services/users') const { createPersonalAccessToken } = require('../services/tokens') const { addOrUpdateStreamCollaborator, @@ -1706,7 +1710,7 @@ describe('GraphQL API Core @core-api', () => { Scopes.Users.Invite ] )}` - await archiveUser({ userId: archivedUser.id }) + await changeUserRole({ userId: archivedUser.id, role: Roles.Server.ArchivedUser }) }) it('Should be able to read public streams', async () => { diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index e49fad519..a984bd748 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -7,7 +7,7 @@ const assert = require('assert') const knex = require('@/db/knex') const { - archiveUser, + changeUserRole, createUser, findOrCreateUser, getUser, @@ -293,7 +293,7 @@ describe('Actors & Tokens @user-services', () => { password: 'nanananananaaaa' }) - await archiveUser({ userId: toBeArchivedId }) + await changeUserRole({ userId: toBeArchivedId, role: Roles.Server.ArchivedUser }) let { users } = await searchUsers('Library', 20, null) expect(users).to.have.lengthOf(1) diff --git a/packages/server/modules/core/tests/usersAdmin.spec.js b/packages/server/modules/core/tests/usersAdmin.spec.js index 082961a03..a8239e894 100644 --- a/packages/server/modules/core/tests/usersAdmin.spec.js +++ b/packages/server/modules/core/tests/usersAdmin.spec.js @@ -6,12 +6,12 @@ const { getUsers, countUsers, deleteUser, - getUserRole, - unmakeUserAdmin, - makeUserAdmin -} = require('../services/users') + changeUserRole, + getUserRole +} = require('@/modules/core/services/users') const { beforeEachContext } = require('@/test/hooks') const { Roles } = require('@speckle/shared') +const cryptoRandomString = require('crypto-random-string') describe('User admin @user-services', () => { const myTestActor = { @@ -53,14 +53,6 @@ describe('User admin @user-services', () => { }) it('Get users query limit is sanitized to upper limit', async () => { - const createNewDroid = (number) => { - return { - name: `${number}`, - email: `${number}@droidarmy.com`, - password: 'sn3aky-1337-b1m' - } - } - const userInputs = Array(250) .fill() .map((v, i) => createNewDroid(i)) @@ -90,27 +82,66 @@ describe('User admin @user-services', () => { expect(await countUsers('droid')).to.equal(250) }) - it('Change user role modifies role', async () => { - const [user] = await getUsers(1, 10) + describe('changeUserRole', () => { + it('throws for invalid role value', async () => { + const role = 'shadow:lurker' + try { + await changeUserRole({ userId: myTestActor.id, role }) + assert.fail('This should have failed') + } catch (err) { + expect(err.message).to.equal(`Invalid role specified: ${role}`) + } + }) + it('throws if guest role not enabled, but trying to change user role to guest', async () => { + const role = Roles.Server.Guest + try { + await changeUserRole({ userId: myTestActor.id, role }) + assert.fail('This should have failed') + } catch (err) { + expect(err.message).to.equal('Guest role is not enabled') + } + }) + it('modifies role', async () => { + const userId = await createUser( + createNewDroid(cryptoRandomString({ length: 13 })) + ) - const oldRole = await getUserRole(user.id) - expect(oldRole).to.equal(Roles.Server.User) + const oldRole = await getUserRole(userId) + expect(oldRole).to.equal(Roles.Server.User) - await makeUserAdmin({ userId: user.id }) - let newRole = await getUserRole(user.id) - expect(newRole).to.equal(Roles.Server.Admin) + await changeUserRole({ userId, role: Roles.Server.Admin }) + let newRole = await getUserRole(userId) + expect(newRole).to.equal(Roles.Server.Admin) - await unmakeUserAdmin({ userId: user.id }) - newRole = await getUserRole(user.id) - expect(newRole).to.equal(Roles.Server.User) - }) + await changeUserRole({ userId, role: Roles.Server.User }) + newRole = await getUserRole(userId) + expect(newRole).to.equal(Roles.Server.User) - it('Ensure at least one admin remains in the server', async () => { - try { - await unmakeUserAdmin({ userId: myTestActor.id, role: Roles.Server.Admin }) - assert.fail('This should have failed') - } catch (err) { - expect(err.message).to.equal('Cannot remove the last admin role from the server') - } + await changeUserRole({ + userId, + role: Roles.Server.Guest, + guestRoleEnabled: true + }) + newRole = await getUserRole(userId) + expect(newRole).to.equal(Roles.Server.Guest) + }) + it('Ensures at least one admin remains in the server', async () => { + try { + await changeUserRole({ userId: myTestActor.id, role: Roles.Server.User }) + assert.fail('This should have failed') + } catch (err) { + expect(err.message).to.equal( + 'Cannot remove the last admin role from the server' + ) + } + }) }) }) + +const createNewDroid = (number) => { + return { + name: `${number}`, + email: `${number}@droidarmy.com`, + password: 'sn3aky-1337-b1m' + } +} diff --git a/packages/server/modules/shared/helpers/envHelper.ts b/packages/server/modules/shared/helpers/envHelper.ts index 91afa4199..94a5edc20 100644 --- a/packages/server/modules/shared/helpers/envHelper.ts +++ b/packages/server/modules/shared/helpers/envHelper.ts @@ -188,6 +188,6 @@ export function ignoreMissingMigrations() { return ['1', 'true'].includes(process.env.IGNORE_MISSING_MIRATIONS || 'false') } -export function enableServerGuests() { +export function guestServerRoleEnabled() { return ['1', 'true'].includes(process.env.ENABLE_SERVER_GUESTS || 'false') } From 424d07303fc99bbe5aa4f23420c0d21e19e6dd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 16:42:44 +0200 Subject: [PATCH 28/43] feat(server): guests cannot own stream projects --- .../services/streams/streamAccessService.js | 11 ++++- .../server/modules/core/tests/streams.spec.ts | 45 ++++++++++++++++--- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/packages/server/modules/core/services/streams/streamAccessService.js b/packages/server/modules/core/services/streams/streamAccessService.js index 1f633aeb6..994349a77 100644 --- a/packages/server/modules/core/services/streams/streamAccessService.js +++ b/packages/server/modules/core/services/streams/streamAccessService.js @@ -2,7 +2,7 @@ const { authorizeResolver } = require(`@/modules/shared`) const { Roles } = require('@/modules/core/helpers/mainConstants') const { LogicError } = require('@/modules/shared/errors') -const { ForbiddenError } = require('apollo-server-express') +const { ForbiddenError, UserInputError } = require('apollo-server-express') const { StreamInvalidAccessError } = require('@/modules/core/errors/stream') const { addStreamPermissionsAddedActivity, @@ -15,6 +15,8 @@ const { grantStreamPermissions } = require('@/modules/core/repositories/streams') +const { ServerAcl } = require('@/modules/core/dbSchema') + /** * Check if user is a stream collaborator * @param {string} userId @@ -129,6 +131,13 @@ async function addOrUpdateStreamCollaborator( await validateStreamAccess(addedById, streamId, Roles.Stream.Owner) + // make sure server guests cannot be stream owners + if (role === Roles.Stream.Owner) { + const userServerRole = await ServerAcl.knex().where({ userId }).first() + if (userServerRole.role === Roles.Server.Guest) + throw new UserInputError('Server guests cannot own streams') + } + const stream = await grantStreamPermissions({ streamId, userId, diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index 19d6c5bd6..b351fe5ac 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -5,9 +5,9 @@ import { updateStream, deleteStream, getStreamUsers, - grantPermissionsStream, - revokePermissionsStream + grantPermissionsStream } from '../services/streams' + import { createBranch, getBranchByNameAndStreamId, @@ -37,7 +37,10 @@ import { createTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper' -import { StreamWithOptionalRole } from '@/modules/core/repositories/streams' +import { + StreamWithOptionalRole, + revokeStreamPermissions +} from '@/modules/core/repositories/streams' import { has, times } from 'lodash' import { Streams } from '@/modules/core/dbSchema' import { ApolloServer } from 'apollo-server-express' @@ -49,6 +52,7 @@ import { GetUserStreamsQuery } from '@/test/graphql/generated/graphql' import { Get } from 'type-fest' +import { changeUserRole } from '@/modules/core/services/users' describe('Streams @core-streams', () => { const userOne: BasicTestUser = { @@ -174,7 +178,7 @@ describe('Streams @core-streams', () => { }) it('Should revoke permissions on stream', async () => { - await revokePermissionsStream({ streamId: testStream.id, userId: userTwo.id }) + await revokeStreamPermissions({ streamId: testStream.id, userId: userTwo.id }) const streamWithRole = await getStream({ streamId: testStream.id, userId: userTwo.id @@ -183,7 +187,7 @@ describe('Streams @core-streams', () => { }) it('Should not revoke owner permissions', async () => { - await revokePermissionsStream({ streamId: testStream.id, userId: userOne.id }) + await revokeStreamPermissions({ streamId: testStream.id, userId: userOne.id }) .then(() => { throw new Error('This should have thrown') }) @@ -215,6 +219,35 @@ describe('Streams @core-streams', () => { const userIsCollaborator = await isStreamCollaborator(userTwo.id, streamId) expect(userIsCollaborator).to.not.be.ok }) + it('Server guests cannot be stream owners', async () => { + const guestGuy: BasicTestUser = { + name: 'Some we do not fully trust', + email: 'shady@contractor.company', + password: 'foobar123', + id: '' + } + + await createTestUsers([guestGuy]) + + await changeUserRole({ + userId: guestGuy.id, + role: Roles.Server.Guest, + guestRoleEnabled: true + }) + + await addOrUpdateStreamCollaborator( + testStream.id, + guestGuy.id, + Roles.Stream.Owner, + userOne.id + ) + .then(() => { + throw new Error('This should have thrown') + }) + .catch((err) => { + expect(err.message).to.include('Server guests cannot own streams') + }) + }) }) describe('`UpdatedAt` prop update', () => { @@ -262,7 +295,7 @@ describe('Streams @core-streams', () => { expect(su!.updatedAt).to.not.equal(lastUpdatedAt) lastUpdatedAt = su!.updatedAt - await revokePermissionsStream({ + await revokeStreamPermissions({ streamId: updatableStream.id, userId: userTwo.id }) From 99bd946f095025967645df056b6c9ba455f5fb8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 17:02:29 +0200 Subject: [PATCH 29/43] feat(server): revert env var based guest role flag we're going for an admin modifiable flag, that needs to be stored in the server config table --- packages/server/modules/core/graph/resolvers/users.js | 4 ++-- packages/server/modules/shared/helpers/envHelper.ts | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 2824fd222..1721ad0fd 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -19,7 +19,6 @@ const { const { Roles, Scopes } = require('@speckle/shared') const { markOnboardingComplete } = require('@/modules/core/repositories/users') const { UsersMeta } = require('@/modules/core/dbSchema') -const { guestServerRoleEnabled } = require('@/modules/shared/helpers/envHelper') /** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */ module.exports = { @@ -136,10 +135,11 @@ module.exports = { }, async userRoleChange(_parent, args) { + const guestRoleEnabled = false await changeUserRole({ role: args.userRoleInput.role, userId: args.userRoleInput.id, - guestRoleEnabled: guestServerRoleEnabled() + guestRoleEnabled }) return true }, diff --git a/packages/server/modules/shared/helpers/envHelper.ts b/packages/server/modules/shared/helpers/envHelper.ts index 94a5edc20..3fc154e28 100644 --- a/packages/server/modules/shared/helpers/envHelper.ts +++ b/packages/server/modules/shared/helpers/envHelper.ts @@ -187,7 +187,3 @@ export function speckleAutomateUrl() { export function ignoreMissingMigrations() { return ['1', 'true'].includes(process.env.IGNORE_MISSING_MIRATIONS || 'false') } - -export function guestServerRoleEnabled() { - return ['1', 'true'].includes(process.env.ENABLE_SERVER_GUESTS || 'false') -} From 7efa0f5ed0eb4ede7ce95e938aa6cc144f755b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 17:06:31 +0200 Subject: [PATCH 30/43] feat(helm): revert guest role values --- utils/helm/speckle-server/templates/server/deployment.yml | 5 ----- utils/helm/speckle-server/values.yaml | 3 --- 2 files changed, 8 deletions(-) diff --git a/utils/helm/speckle-server/templates/server/deployment.yml b/utils/helm/speckle-server/templates/server/deployment.yml index 8f70b96c6..9d7bd9621 100644 --- a/utils/helm/speckle-server/templates/server/deployment.yml +++ b/utils/helm/speckle-server/templates/server/deployment.yml @@ -209,11 +209,6 @@ spec: key: {{ default "google_client_secret" .Values.server.auth.google.clientSecret.secretKey }} {{- end }} - {{- if .Values.serverGuestsEnabled }} - - name: ENABLE_SERVER_GUESTS - value: true - {{- end }} - # Github Auth {{- if .Values.server.auth.github.enabled }} - name: STRATEGY_GITHUB diff --git a/utils/helm/speckle-server/values.yaml b/utils/helm/speckle-server/values.yaml index 079a3e70f..d9fabb523 100644 --- a/utils/helm/speckle-server/values.yaml +++ b/utils/helm/speckle-server/values.yaml @@ -84,9 +84,6 @@ secretName: server-vars ## file_size_limit_mb: 100 -## @param serverGuestsEnabled Enables users to join the server as Guests, having limiter permissions -serverGuestsEnabled: false - ## @section Monitoring ## @descriptionStart ## This enables metrics generated by Speckle to be ingested by Prometheus: https://prometheus.io/ From 935ebaf60a47ea3eddc78fe8a2ac14d00d5e3be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 17:30:52 +0200 Subject: [PATCH 31/43] feat(server): add guest mode as server info field --- .../server/assets/core/typedefs/server.graphql | 2 ++ .../modules/core/graph/generated/graphql.ts | 3 +++ packages/server/modules/core/helpers/types.ts | 1 + .../migrations/20230727150957_serverGuestMode.ts | 16 ++++++++++++++++ packages/server/modules/core/services/generic.js | 5 ++++- .../server/test/graphql/generated/graphql.ts | 2 ++ 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/server/modules/core/migrations/20230727150957_serverGuestMode.ts diff --git a/packages/server/assets/core/typedefs/server.graphql b/packages/server/assets/core/typedefs/server.graphql index 0fa9f811c..db1b00e94 100644 --- a/packages/server/assets/core/typedefs/server.graphql +++ b/packages/server/assets/core/typedefs/server.graphql @@ -15,6 +15,7 @@ type ServerInfo { roles: [Role]! scopes: [Scope]! inviteOnly: Boolean + guestModeEnabled: Boolean! version: String } @@ -48,4 +49,5 @@ input ServerInfoUpdateInput { adminContact: String termsOfService: String inviteOnly: Boolean + guestModeEnabled: Boolean } diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index e27170db0..d25e36be6 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -1846,6 +1846,7 @@ export type ServerInfo = { canonicalUrl?: Maybe; company?: Maybe; description?: Maybe; + guestModeEnabled: Scalars['Boolean']; inviteOnly?: Maybe; name: Scalars['String']; roles: Array>; @@ -1858,6 +1859,7 @@ export type ServerInfoUpdateInput = { adminContact?: InputMaybe; company?: InputMaybe; description?: InputMaybe; + guestModeEnabled?: InputMaybe; inviteOnly?: InputMaybe; name: Scalars['String']; termsOfService?: InputMaybe; @@ -3652,6 +3654,7 @@ export type ServerInfoResolvers, ParentType, ContextType>; company?: Resolver, ParentType, ContextType>; description?: Resolver, ParentType, ContextType>; + guestModeEnabled?: Resolver; inviteOnly?: Resolver, ParentType, ContextType>; name?: Resolver; roles?: Resolver>, ParentType, ContextType>; diff --git a/packages/server/modules/core/helpers/types.ts b/packages/server/modules/core/helpers/types.ts index 1364dd524..8f8be2c91 100644 --- a/packages/server/modules/core/helpers/types.ts +++ b/packages/server/modules/core/helpers/types.ts @@ -73,6 +73,7 @@ export type ServerConfigRecord = { canonicalUrl: string completed: boolean inviteOnly: boolean + guestModeEnabled: boolean } export type ServerInfo = ServerConfigRecord & { diff --git a/packages/server/modules/core/migrations/20230727150957_serverGuestMode.ts b/packages/server/modules/core/migrations/20230727150957_serverGuestMode.ts new file mode 100644 index 000000000..1ba1e7eda --- /dev/null +++ b/packages/server/modules/core/migrations/20230727150957_serverGuestMode.ts @@ -0,0 +1,16 @@ +import { Knex } from 'knex' + +const TABLE_NAME = 'server_config' +const COL_NAME = 'guestModeEnabled' + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable(TABLE_NAME, (table) => { + table.boolean(COL_NAME).defaultTo(false).notNullable() + }) +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable(TABLE_NAME, (table) => { + table.dropColumn(COL_NAME) + }) +} diff --git a/packages/server/modules/core/services/generic.js b/packages/server/modules/core/services/generic.js index b240049cc..ef749fdce 100644 --- a/packages/server/modules/core/services/generic.js +++ b/packages/server/modules/core/services/generic.js @@ -38,7 +38,8 @@ module.exports = { description, adminContact, termsOfService, - inviteOnly + inviteOnly, + guestModeEnabled }) { const serverInfo = await Info().select('*').first() if (!serverInfo) @@ -49,6 +50,7 @@ module.exports = { adminContact, termsOfService, inviteOnly, + guestModeEnabled, completed: true }) else @@ -59,6 +61,7 @@ module.exports = { adminContact, termsOfService, inviteOnly, + guestModeEnabled, completed: true }) } diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 42d86872c..305e24a37 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -1837,6 +1837,7 @@ export type ServerInfo = { canonicalUrl?: Maybe; company?: Maybe; description?: Maybe; + guestModeEnabled: Scalars['Boolean']; inviteOnly?: Maybe; name: Scalars['String']; roles: Array>; @@ -1849,6 +1850,7 @@ export type ServerInfoUpdateInput = { adminContact?: InputMaybe; company?: InputMaybe; description?: InputMaybe; + guestModeEnabled?: InputMaybe; inviteOnly?: InputMaybe; name: Scalars['String']; termsOfService?: InputMaybe; From 42190f42eed30040aa443078fe8180e02d210bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Thu, 27 Jul 2023 17:39:19 +0200 Subject: [PATCH 32/43] feat(server): user role change uses server info to get guest mode flag --- packages/server/modules/core/graph/resolvers/users.js | 5 +++-- packages/server/modules/core/services/users.js | 4 ++-- packages/server/modules/core/tests/usersAdmin.spec.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 1721ad0fd..4df63920a 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -19,6 +19,7 @@ const { const { Roles, Scopes } = require('@speckle/shared') const { markOnboardingComplete } = require('@/modules/core/repositories/users') const { UsersMeta } = require('@/modules/core/dbSchema') +const { getServerInfo } = require('@/modules/core/services/generic') /** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */ module.exports = { @@ -135,11 +136,11 @@ module.exports = { }, async userRoleChange(_parent, args) { - const guestRoleEnabled = false + const { guestModeEnabled } = await getServerInfo() await changeUserRole({ role: args.userRoleInput.role, userId: args.userRoleInput.id, - guestRoleEnabled + guestModeEnabled }) return true }, diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index 4eeae7bf3..be9ea172f 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -270,10 +270,10 @@ module.exports = { return parseInt(userCount.count) }, - async changeUserRole({ userId, role, guestRoleEnabled = false }) { + async changeUserRole({ userId, role, guestModeEnabled = false }) { if (!Object.values(Roles.Server).includes(role)) throw new UserInputError(`Invalid role specified: ${role}`) - if (!guestRoleEnabled && role === Roles.Server.Guest) + if (!guestModeEnabled && role === Roles.Server.Guest) throw new UserInputError('Guest role is not enabled') if (role !== Roles.Server.Admin) await _ensureAtleastOneAdminRemains(userId) await _changeUserRole({ userId, role }) diff --git a/packages/server/modules/core/tests/usersAdmin.spec.js b/packages/server/modules/core/tests/usersAdmin.spec.js index a8239e894..de9162192 100644 --- a/packages/server/modules/core/tests/usersAdmin.spec.js +++ b/packages/server/modules/core/tests/usersAdmin.spec.js @@ -120,7 +120,7 @@ describe('User admin @user-services', () => { await changeUserRole({ userId, role: Roles.Server.Guest, - guestRoleEnabled: true + guestModeEnabled: true }) newRole = await getUserRole(userId) expect(newRole).to.equal(Roles.Server.Guest) From 2a70557903d33baf1aecb47d091c12d49ef74e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Fri, 28 Jul 2023 09:44:13 +0200 Subject: [PATCH 33/43] fix(server, helm): minor fixes --- .../modules/notifications/tests/activityDigest.spec.ts | 3 ++- utils/helm/speckle-server/values.schema.json | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/server/modules/notifications/tests/activityDigest.spec.ts b/packages/server/modules/notifications/tests/activityDigest.spec.ts index ff5e4d20c..4c7cf2798 100644 --- a/packages/server/modules/notifications/tests/activityDigest.spec.ts +++ b/packages/server/modules/notifications/tests/activityDigest.spec.ts @@ -49,7 +49,8 @@ describe('Activity digest notifications @notifications', () => { canonicalUrl: 'this would be localhost:// or whatever', completed: false, inviteOnly: true, - version: 'testing 1 2 3' + version: 'testing 1 2 3', + guestModeEnabled: false } const topic: DigestTopic = { diff --git a/utils/helm/speckle-server/values.schema.json b/utils/helm/speckle-server/values.schema.json index b9491a413..850a6e2ba 100644 --- a/utils/helm/speckle-server/values.schema.json +++ b/utils/helm/speckle-server/values.schema.json @@ -97,11 +97,6 @@ "description": "This maximum size of any single file (unit is Megabytes) that can be uploaded to Speckle", "default": 100 }, - "serverGuestsEnabled": { - "type": "boolean", - "description": "Enables users to join the server as Guests, having limiter permissions", - "default": false - }, "enable_prometheus_monitoring": { "type": "boolean", "description": "If enabled, Speckle deploys a Prometheus ServiceMonitor resource", From 172357dfd13e37782e95defff5ed93805eadfa62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Fri, 28 Jul 2023 11:15:52 +0200 Subject: [PATCH 34/43] refactor(server): use auth pipeline server role validator for gql auth --- packages/server/modules/shared/index.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index db2c8d31e..ed25edc79 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -8,6 +8,10 @@ const { BranchSubscriptions } = require('@/modules/shared/utils/subscriptions') const { Roles } = require('@speckle/shared') +const { + validateServerRole: authPipelineValidateServerRole, + authHasFailed +} = require('@/modules/shared/authz') const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') const { ServerAcl: ServerAclSchema } = require('@/modules/core/dbSchema') @@ -27,21 +31,11 @@ const getRoles = async () => { * @param {string} requiredRole */ async function validateServerRole(context, requiredRole) { - const roles = await getRoles() - - if (!context.auth) throw new ForbiddenError('You must provide an auth token.') - - const role = roles.find((r) => r.name === requiredRole) - const myRole = roles.find((r) => r.name === context.role) - - if (!role) throw new ApolloError('Invalid server role specified') - if (!myRole) - throw new ForbiddenError('You do not have the required server role (null)') - - if (context.role === Roles.Server.Admin) return true - if (myRole.weight >= role.weight) return true - - throw new ForbiddenError('You do not have the required server role') + const { authResult } = await authPipelineValidateServerRole({ requiredRole })({ + context + }) + if (authHasFailed(authResult)) throw authResult.error + return true } /** From 7a92730a0d8df552fdd165993415d1edc87d75a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Fri, 28 Jul 2023 11:20:03 +0200 Subject: [PATCH 35/43] fix(server auth): make sure to not throw null --- packages/server/modules/shared/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index ed25edc79..1ea34e504 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -34,7 +34,8 @@ async function validateServerRole(context, requiredRole) { const { authResult } = await authPipelineValidateServerRole({ requiredRole })({ context }) - if (authHasFailed(authResult)) throw authResult.error + if (authHasFailed(authResult)) + throw authResult.error ?? new Error('Auth failed without an error') return true } From 7bad071e972cb57e6570eccb11cb874207ac20a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Fri, 28 Jul 2023 11:57:28 +0200 Subject: [PATCH 36/43] fix(server tests): its guestModeEnabled now --- packages/server/modules/core/tests/streams.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index b351fe5ac..2e949f800 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -232,7 +232,7 @@ describe('Streams @core-streams', () => { await changeUserRole({ userId: guestGuy.id, role: Roles.Server.Guest, - guestRoleEnabled: true + guestModeEnabled: true }) await addOrUpdateStreamCollaborator( From e9d9fc770ccd8008f5cff60930068353be6f8262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Mon, 31 Jul 2023 12:29:43 +0200 Subject: [PATCH 37/43] allow guest access to selected gql resources --- .../typedefs/accessrequests.graphql | 6 ++-- .../activitystream/typedefs/activity.graphql | 18 +++++++---- .../server/assets/auth/typedefs/apps.graphql | 6 ++-- .../assets/comments/typedefs/comments.gql | 20 ++++++------ .../assets/comments/typedefs/viewer.gql | 2 +- .../core/typedefs/branchesAndCommits.graphql | 32 +++++++++---------- .../core/typedefs/modelsAndVersions.graphql | 4 +-- .../assets/core/typedefs/streams.graphql | 20 ++++++------ .../server/assets/core/typedefs/user.graphql | 6 ++-- .../assets/emails/typedefs/emails.graphql | 2 +- .../typedefs/notificationPreferences.graphql | 2 +- .../typedefs/serverInvites.graphql | 6 ++-- .../modules/core/graph/resolvers/objects.js | 2 +- .../modules/core/graph/resolvers/projects.ts | 2 +- .../modules/core/graph/resolvers/streams.js | 2 +- .../modules/core/graph/resolvers/users.js | 10 +++--- 16 files changed, 72 insertions(+), 68 deletions(-) diff --git a/packages/server/assets/accessrequests/typedefs/accessrequests.graphql b/packages/server/assets/accessrequests/typedefs/accessrequests.graphql index 3c9a270d9..8fb239038 100644 --- a/packages/server/assets/accessrequests/typedefs/accessrequests.graphql +++ b/packages/server/assets/accessrequests/typedefs/accessrequests.graphql @@ -3,7 +3,7 @@ extend type Query { Get authed user's stream access request """ streamAccessRequest(streamId: String!): StreamAccessRequest - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) } extend type Stream { @@ -21,13 +21,13 @@ extend type Mutation { requestId: String! accept: Boolean! role: StreamRole! = STREAM_CONTRIBUTOR - ): Boolean! @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:invite") + ): Boolean! @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "users:invite") """ Request access to a specific stream """ streamAccessRequestCreate(streamId: String!): StreamAccessRequest! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "users:invite") } diff --git a/packages/server/assets/activitystream/typedefs/activity.graphql b/packages/server/assets/activitystream/typedefs/activity.graphql index c648928c6..5783cf0a2 100644 --- a/packages/server/assets/activitystream/typedefs/activity.graphql +++ b/packages/server/assets/activitystream/typedefs/activity.graphql @@ -8,7 +8,9 @@ extend type User { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:read") + ): ActivityCollection + @hasServerRole(role: SERVER_GUEST) + @hasScope(scope: "users:read") """ The user's timeline in chronological order @@ -19,7 +21,7 @@ extend type User { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScopes(scopes: ["users:read", "streams:read"]) } @@ -33,7 +35,9 @@ extend type LimitedUser { before: DateTime cursor: DateTime limit: Int! = 25 - ): ActivityCollection @hasServerRole(role: SERVER_USER) @hasScope(scope: "users:read") + ): ActivityCollection + @hasServerRole(role: SERVER_GUEST) + @hasScope(scope: "users:read") """ The user's timeline in chronological order @@ -44,7 +48,7 @@ extend type LimitedUser { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScopes(scopes: ["users:read", "streams:read"]) } @@ -59,7 +63,7 @@ extend type Stream { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } @@ -74,7 +78,7 @@ extend type Branch { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } @@ -89,7 +93,7 @@ extend type Commit { cursor: DateTime limit: Int! = 25 ): ActivityCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/auth/typedefs/apps.graphql b/packages/server/assets/auth/typedefs/apps.graphql index 85ce003e9..d0d30d168 100644 --- a/packages/server/assets/auth/typedefs/apps.graphql +++ b/packages/server/assets/auth/typedefs/apps.graphql @@ -47,14 +47,14 @@ extend type User { Returns the apps you have authorized. """ authorizedApps: [ServerAppListItem] - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "apps:read") """ Returns the apps you have created. """ createdApps: [ServerApp!] - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "apps:read") } @@ -84,7 +84,7 @@ extend type Mutation { Revokes (de-authorizes) an application that you have previously authorized. """ appRevokeAccess(appId: String!): Boolean - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "apps:write") } diff --git a/packages/server/assets/comments/typedefs/comments.gql b/packages/server/assets/comments/typedefs/comments.gql index 0434b9c7d..4244aef30 100644 --- a/packages/server/assets/comments/typedefs/comments.gql +++ b/packages/server/assets/comments/typedefs/comments.gql @@ -324,7 +324,7 @@ type CommentMutations { } extend type Mutation { - commentMutations: CommentMutations! @hasServerRole(role: SERVER_USER) + commentMutations: CommentMutations! @hasServerRole(role: SERVER_GUEST) """ Used for broadcasting real time chat head bubbles and status. Does not persist any info. @@ -334,7 +334,7 @@ extend type Mutation { resourceId: String! data: JSONObject ): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @deprecated(reason: "Use broadcastViewerUserActivity") """ @@ -345,14 +345,14 @@ extend type Mutation { commentId: String! data: JSONObject ): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @deprecated(reason: "Use broadcastViewerUserActivity") """ Creates a comment """ commentCreate(input: CommentCreateInput!): String! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -360,7 +360,7 @@ extend type Mutation { Flags a comment as viewed by you (the logged in user). """ commentView(streamId: String!, commentId: String!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -372,7 +372,7 @@ extend type Mutation { commentId: String! archived: Boolean! = true ): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -380,7 +380,7 @@ extend type Mutation { Edits a comment. """ commentEdit(input: CommentEditInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") @@ -388,7 +388,7 @@ extend type Mutation { Adds a reply to a comment. """ commentReply(input: ReplyCreateInput!): String! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use commentMutations version") } @@ -457,7 +457,7 @@ extend type Subscription { - for a specific resource/set of resources: pass in a list of resourceIds (commit or object ids); this sub will get called when *any* of the resources provided get a comment. """ commentActivity(streamId: String!, resourceIds: [String]): CommentActivityMessage! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated(reason: "Use projectCommentsUpdated") @@ -470,7 +470,7 @@ extend type Subscription { streamId: String! commentId: String! ): CommentThreadActivityMessage! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") @deprecated( reason: "Use projectCommentsUpdated or viewerUserActivityBroadcasted for reply status" diff --git a/packages/server/assets/comments/typedefs/viewer.gql b/packages/server/assets/comments/typedefs/viewer.gql index 0d5b41d79..41f6b0e18 100644 --- a/packages/server/assets/comments/typedefs/viewer.gql +++ b/packages/server/assets/comments/typedefs/viewer.gql @@ -46,7 +46,7 @@ extend type Mutation { projectId: String! resourceIdString: String! message: ViewerUserActivityMessageInput! - ): Boolean! @hasServerRole(role: SERVER_USER) + ): Boolean! @hasServerRole(role: SERVER_GUEST) } extend type Subscription { diff --git a/packages/server/assets/core/typedefs/branchesAndCommits.graphql b/packages/server/assets/core/typedefs/branchesAndCommits.graphql index 435d63a65..dc0d97469 100644 --- a/packages/server/assets/core/typedefs/branchesAndCommits.graphql +++ b/packages/server/assets/core/typedefs/branchesAndCommits.graphql @@ -48,7 +48,7 @@ type Commit { Will throw an authorization error if active user isn't authorized to see it, for example, if a stream isn't public and the user doesn't have the appropriate rights. """ - stream: Stream! @hasServerRole(role: SERVER_USER) @hasScope(scope: "streams:read") + stream: Stream! @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } type BranchCollection { @@ -65,40 +65,40 @@ type CommitCollection { extend type Mutation { branchCreate(branch: BranchCreateInput!): String! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") branchUpdate(branch: BranchUpdateInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") branchDelete(branch: BranchDeleteInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") commitCreate(commit: CommitCreateInput!): String! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") commitUpdate(commit: CommitUpdateInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") commitReceive(input: CommitReceivedInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") commitDelete(commit: CommitDeleteInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") """ Move a batch of commits to a new branch """ commitsMove(input: CommitsMoveInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") """ Delete a batch of commits """ commitsDelete(input: CommitsDeleteInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") } @@ -108,38 +108,38 @@ extend type Subscription { Subscribe to branch created event """ branchCreated(streamId: String!): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribe to branch updated event. """ branchUpdated(streamId: String!, branchId: String): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribe to branch deleted event """ branchDeleted(streamId: String!): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribe to commit created event """ commitCreated(streamId: String!): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribe to commit updated event. """ commitUpdated(streamId: String!, commitId: String): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribe to commit deleted event """ commitDeleted(streamId: String!): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/core/typedefs/modelsAndVersions.graphql b/packages/server/assets/core/typedefs/modelsAndVersions.graphql index 48c71471a..960bcbba9 100644 --- a/packages/server/assets/core/typedefs/modelsAndVersions.graphql +++ b/packages/server/assets/core/typedefs/modelsAndVersions.graphql @@ -164,11 +164,11 @@ type VersionMutations { extend type Mutation { modelMutations: ModelMutations! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") versionMutations: VersionMutations! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:write") } diff --git a/packages/server/assets/core/typedefs/streams.graphql b/packages/server/assets/core/typedefs/streams.graphql index c7e962d76..e48f262f0 100644 --- a/packages/server/assets/core/typedefs/streams.graphql +++ b/packages/server/assets/core/typedefs/streams.graphql @@ -10,7 +10,7 @@ extend type Query { Pass in the `query` parameter to search by name, description or ID. """ streams(query: String, limit: Int = 25, cursor: String): StreamCollection - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ @@ -77,7 +77,7 @@ extend type User { authenticated user, then this will only return discoverable streams. """ streams(limit: Int! = 25, cursor: String): StreamCollection! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ @@ -85,7 +85,7 @@ extend type User { Note: You can't use this to retrieve another user's favorite streams. """ favoriteStreams(limit: Int! = 25, cursor: String): StreamCollection! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ @@ -99,7 +99,7 @@ extend type LimitedUser { Returns all discoverable streams that the user is a collaborator on """ streams(limit: Int! = 25, cursor: String): StreamCollection! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ @@ -181,12 +181,12 @@ extend type Mutation { # Favorite/unfavorite the given stream streamFavorite(streamId: String!, favorited: Boolean!): Stream - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) """ Remove yourself from stream collaborators (not possible for the owner) """ - streamLeave(streamId: String!): Boolean! @hasServerRole(role: SERVER_USER) + streamLeave(streamId: String!): Boolean! @hasServerRole(role: SERVER_GUEST) } extend type Subscription { @@ -200,7 +200,7 @@ extend type Subscription { **NOTE**: If someone shares a stream with you, this subscription will be triggered with an extra value of `sharedBy` in the payload. """ userStreamAdded: JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "profile:read") """ @@ -208,7 +208,7 @@ extend type Subscription { **NOTE**: If someone revokes your permissions on a stream, this subscription will be triggered with an extra value of `revokedBy` in the payload. """ userStreamRemoved: JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "profile:read") # @@ -220,14 +220,14 @@ extend type Subscription { Subscribes to stream updated event. Use this in clients/components that pertain only to this stream. """ streamUpdated(streamId: String): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") """ Subscribes to stream deleted event. Use this in clients/components that pertain only to this stream. """ streamDeleted(streamId: String): JSONObject - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } diff --git a/packages/server/assets/core/typedefs/user.graphql b/packages/server/assets/core/typedefs/user.graphql index 1fd08bc7f..ef6f9f2cd 100644 --- a/packages/server/assets/core/typedefs/user.graphql +++ b/packages/server/assets/core/typedefs/user.graphql @@ -8,7 +8,7 @@ extend type Query { Get the (limited) profile information of another server user """ otherUser(id: String!): LimitedUser - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "users:read") """ @@ -153,7 +153,7 @@ extend type Mutation { Delete a user's account. """ userDelete(userConfirmation: UserDeleteInput!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "profile:delete") adminDeleteUser(userConfirmation: UserDeleteInput!): Boolean! @@ -165,7 +165,7 @@ extend type Mutation { """ Various Active User oriented mutations """ - activeUserMutations: ActiveUserMutations! @hasServerRole(role: SERVER_USER) + activeUserMutations: ActiveUserMutations! @hasServerRole(role: SERVER_GUEST) } input UserRoleInput { diff --git a/packages/server/assets/emails/typedefs/emails.graphql b/packages/server/assets/emails/typedefs/emails.graphql index 7f601d476..a0deb8516 100644 --- a/packages/server/assets/emails/typedefs/emails.graphql +++ b/packages/server/assets/emails/typedefs/emails.graphql @@ -9,5 +9,5 @@ extend type Mutation { """ (Re-)send the account verification e-mail """ - requestVerification: Boolean! @hasServerRole(role: SERVER_USER) + requestVerification: Boolean! @hasServerRole(role: SERVER_GUEST) } diff --git a/packages/server/assets/notifications/typedefs/notificationPreferences.graphql b/packages/server/assets/notifications/typedefs/notificationPreferences.graphql index a7ea36a71..0e610d991 100644 --- a/packages/server/assets/notifications/typedefs/notificationPreferences.graphql +++ b/packages/server/assets/notifications/typedefs/notificationPreferences.graphql @@ -4,5 +4,5 @@ extend type User { extend type Mutation { userNotificationPreferencesUpdate(preferences: JSONObject!): Boolean - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) } diff --git a/packages/server/assets/serverinvites/typedefs/serverInvites.graphql b/packages/server/assets/serverinvites/typedefs/serverInvites.graphql index d58562c98..0af142010 100644 --- a/packages/server/assets/serverinvites/typedefs/serverInvites.graphql +++ b/packages/server/assets/serverinvites/typedefs/serverInvites.graphql @@ -25,13 +25,13 @@ extend type Mutation { Accept or decline a stream invite """ streamInviteUse(accept: Boolean!, streamId: String!, token: String!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) """ Cancel a pending stream invite. Can only be invoked by a stream owner. """ streamInviteCancel(streamId: String!, inviteId: String!): Boolean! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "users:invite") """ @@ -66,7 +66,7 @@ extend type Query { Get all invitations to streams that the active user has """ streamInvites: [PendingStreamCollaborator!]! - @hasServerRole(role: SERVER_USER) + @hasServerRole(role: SERVER_GUEST) @hasScope(scope: "streams:read") } diff --git a/packages/server/modules/core/graph/resolvers/objects.js b/packages/server/modules/core/graph/resolvers/objects.js index 0b9eb0e5c..bc9b28901 100644 --- a/packages/server/modules/core/graph/resolvers/objects.js +++ b/packages/server/modules/core/graph/resolvers/objects.js @@ -60,7 +60,7 @@ module.exports = { }, Mutation: { async objectCreate(parent, args, context) { - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Streams.Write) await authorizeResolver( context.userId, diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index 6a0b4d63a..acefd66f0 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -52,7 +52,7 @@ export = { await authorizeResolver(context.userId, args.id, Roles.Stream.Reviewer) if (!stream.isPublic) { - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) validateScopes(context.scopes, Scopes.Streams.Read) } diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index 941ba633d..d5fd76b34 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -88,7 +88,7 @@ module.exports = { await authorizeResolver(context.userId, args.id, Roles.Stream.Reviewer) if (!stream.isPublic) { - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Streams.Read) } diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 4df63920a..04e3e8556 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -32,7 +32,7 @@ module.exports = { if (!activeUserId) return null // Only if authenticated - check for server roles & scopes - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Read) return await getUser(activeUserId) @@ -46,7 +46,7 @@ module.exports = { // User wants info about himself and he's not authenticated - just return null if (!context.auth && !args.id) return null - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) if (!args.id) await validateScopes(context.scopes, Scopes.Profile.Read) else await validateScopes(context.scopes, Scopes.Users.Read) @@ -63,7 +63,7 @@ module.exports = { }, async userSearch(parent, args, context) { - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Read) await validateScopes(context.scopes, Scopes.Users.Read) @@ -130,7 +130,7 @@ module.exports = { }, Mutation: { async userUpdate(_parent, args, context) { - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await updateUserAndNotify(context.userId, args.user) return true }, @@ -162,7 +162,7 @@ module.exports = { // The below are not really needed anymore as we've added the hasRole and hasScope // directives in the graphql schema itself. // Since I am paranoid, I'll leave them here too. - await validateServerRole(context, Roles.Server.User) + await validateServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Delete) await deleteUser(context.userId, args.user) From 3e1c2dd42d17a2b37eb6367633e1865034d2e09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Mon, 31 Jul 2023 12:41:04 +0200 Subject: [PATCH 38/43] fix(server): its called guest mode now --- packages/server/modules/core/tests/streams.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index b351fe5ac..2e949f800 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -232,7 +232,7 @@ describe('Streams @core-streams', () => { await changeUserRole({ userId: guestGuy.id, role: Roles.Server.Guest, - guestRoleEnabled: true + guestModeEnabled: true }) await addOrUpdateStreamCollaborator( From 8d6b73fb797b28d5fd4da9a9774c8daf8439ee8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Mon, 31 Jul 2023 12:50:28 +0200 Subject: [PATCH 39/43] feat(server): allow guests rest api access --- packages/server/modules/core/rest/authUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index 3327b73a9..fbb80ad75 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -14,7 +14,7 @@ module.exports = { if (stream?.isPublic) return { result: true, status: 200 } try { - await validateServerRole(req.context, Roles.Server.User) + await validateServerRole(req.context, Roles.Server.Guest) } catch (err) { return { result: false, status: 401 } } @@ -47,7 +47,7 @@ module.exports = { } try { - await validateServerRole(req.context, Roles.Server.User) + await validateServerRole(req.context, Roles.Server.Guest) } catch (err) { return { result: false, status: 401 } } From cb3dbb7a9d90449f456f717e4e2d2d54573c59c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 1 Aug 2023 12:11:47 +0200 Subject: [PATCH 40/43] refactor(server/shared): reduce (but not fix) circular dependencies --- .../activitystream/services/branchActivity.ts | 5 ++++- .../activitystream/services/commentActivity.ts | 2 +- .../activitystream/services/commitActivity.ts | 5 ++++- .../activitystream/services/streamActivity.ts | 5 ++++- .../modules/comments/graph/resolvers/comments.js | 2 +- .../server/modules/core/graph/resolvers/branches.js | 6 +++++- .../server/modules/core/graph/resolvers/commits.js | 6 +++++- .../server/modules/core/graph/resolvers/streams.js | 7 +++++-- packages/server/modules/shared/authz.ts | 2 +- packages/server/modules/shared/index.js | 11 ++--------- packages/server/modules/shared/roles.js | 12 ++++++++++++ 11 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 packages/server/modules/shared/roles.js diff --git a/packages/server/modules/activitystream/services/branchActivity.ts b/packages/server/modules/activitystream/services/branchActivity.ts index 6f5635f41..3ffd19f29 100644 --- a/packages/server/modules/activitystream/services/branchActivity.ts +++ b/packages/server/modules/activitystream/services/branchActivity.ts @@ -1,7 +1,10 @@ import { saveActivity } from '@/modules/activitystream/services' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { BranchRecord } from '@/modules/core/helpers/types' -import { pubsub, BranchPubsubEvents } from '@/modules/shared' +import { + pubsub, + BranchSubscriptions as BranchPubsubEvents +} from '@/modules/shared/utils/subscriptions' import { BranchDeleteInput, BranchUpdateInput, diff --git a/packages/server/modules/activitystream/services/commentActivity.ts b/packages/server/modules/activitystream/services/commentActivity.ts index e20842529..6ba8c8300 100644 --- a/packages/server/modules/activitystream/services/commentActivity.ts +++ b/packages/server/modules/activitystream/services/commentActivity.ts @@ -14,7 +14,7 @@ import { getViewerResourcesForComment, getViewerResourcesFromLegacyIdentifiers } from '@/modules/core/services/commit/viewerResources' -import { pubsub } from '@/modules/shared' +import { pubsub } from '@/modules/shared/utils/subscriptions' import { CommentSubscriptions, ProjectSubscriptions, diff --git a/packages/server/modules/activitystream/services/commitActivity.ts b/packages/server/modules/activitystream/services/commitActivity.ts index 7b932a134..7341a1d60 100644 --- a/packages/server/modules/activitystream/services/commitActivity.ts +++ b/packages/server/modules/activitystream/services/commitActivity.ts @@ -1,6 +1,9 @@ import { saveActivity } from '@/modules/activitystream/services' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' -import { CommitPubsubEvents, pubsub } from '@/modules/shared' +import { + CommitSubscriptions as CommitPubsubEvents, + pubsub +} from '@/modules/shared/utils/subscriptions' import { CommitCreateInput, CommitReceivedInput, diff --git a/packages/server/modules/activitystream/services/streamActivity.ts b/packages/server/modules/activitystream/services/streamActivity.ts index 198301605..2d9413f12 100644 --- a/packages/server/modules/activitystream/services/streamActivity.ts +++ b/packages/server/modules/activitystream/services/streamActivity.ts @@ -1,7 +1,10 @@ import { saveActivity } from '@/modules/activitystream/services' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { StreamRoles } from '@/modules/core/helpers/mainConstants' -import { pubsub, StreamPubsubEvents } from '@/modules/shared' +import { + pubsub, + StreamSubscriptions as StreamPubsubEvents +} from '@/modules/shared/utils/subscriptions' import { StreamCreateInput } from '@/test/graphql/generated/graphql' import { Knex } from 'knex' import { getStreamCollaborators } from '@/modules/core/repositories/streams' diff --git a/packages/server/modules/comments/graph/resolvers/comments.js b/packages/server/modules/comments/graph/resolvers/comments.js index c9b57174b..739642099 100644 --- a/packages/server/modules/comments/graph/resolvers/comments.js +++ b/packages/server/modules/comments/graph/resolvers/comments.js @@ -1,4 +1,4 @@ -const { pubsub } = require('@/modules/shared') +const { pubsub } = require('@/modules/shared/utils/subscriptions') const { ForbiddenError: ApolloForbiddenError } = require('apollo-server-express') const { ForbiddenError } = require('@/modules/shared/errors') const { getStream } = require('@/modules/core/services/streams') diff --git a/packages/server/modules/core/graph/resolvers/branches.js b/packages/server/modules/core/graph/resolvers/branches.js index c1be66e98..93e5e51dd 100644 --- a/packages/server/modules/core/graph/resolvers/branches.js +++ b/packages/server/modules/core/graph/resolvers/branches.js @@ -2,7 +2,11 @@ const { withFilter } = require('graphql-subscriptions') -const { authorizeResolver, pubsub, BranchPubsubEvents } = require('@/modules/shared') +const { + pubsub, + BranchSubscriptions: BranchPubsubEvents +} = require('@/modules/shared/utils/subscriptions') +const { authorizeResolver } = require('@/modules/shared') const { getBranchByNameAndStreamId, getBranchById } = require('../../services/branches') const { diff --git a/packages/server/modules/core/graph/resolvers/commits.js b/packages/server/modules/core/graph/resolvers/commits.js index 4a20886cd..5e6fc50bc 100644 --- a/packages/server/modules/core/graph/resolvers/commits.js +++ b/packages/server/modules/core/graph/resolvers/commits.js @@ -2,7 +2,11 @@ const { UserInputError, ApolloError } = require('apollo-server-express') const { withFilter } = require('graphql-subscriptions') -const { authorizeResolver, pubsub, CommitPubsubEvents } = require('@/modules/shared') +const { + pubsub, + CommitSubscriptions: CommitPubsubEvents +} = require('@/modules/shared/utils/subscriptions') +const { authorizeResolver } = require('@/modules/shared') const { getCommitById, diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index d5fd76b34..69dad153e 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -14,9 +14,12 @@ const { } = require('@/modules/core/services/streams') const { - authorizeResolver, pubsub, - StreamPubsubEvents, + StreamSubscriptions: StreamPubsubEvents +} = require(`@/modules/shared/utils/subscriptions`) + +const { + authorizeResolver, validateScopes, validateServerRole } = require(`@/modules/shared`) diff --git a/packages/server/modules/shared/authz.ts b/packages/server/modules/shared/authz.ts index c7e8c74e4..7a217934c 100644 --- a/packages/server/modules/shared/authz.ts +++ b/packages/server/modules/shared/authz.ts @@ -4,7 +4,7 @@ import { ServerRoles, StreamRoles } from '@/modules/core/helpers/mainConstants' -import { getRoles } from '@/modules/shared' +import { getRoles } from '@/modules/shared/roles' import { getStream } from '@/modules/core/services/streams' import { diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index 1ea34e504..9aa32777f 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -15,16 +15,9 @@ const { const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') const { ServerAcl: ServerAclSchema } = require('@/modules/core/dbSchema') +const { getRoles } = require('@/modules/shared/roles') const ServerAcl = () => ServerAclSchema.knex() -let roles - -const getRoles = async () => { - if (roles) return roles - roles = await knex('user_roles').select('*') - return roles -} - /** * Validates a server role against the req's context object. * @param {import('@/modules/shared/helpers/typeHelper').GraphQLContext} context @@ -60,7 +53,7 @@ async function validateScopes(scopes, scope) { async function authorizeResolver(userId, resourceId, requiredRole) { userId = userId || null - if (!roles) roles = await knex('user_roles').select('*') + const roles = getRoles() // TODO: Cache these results with a TTL of 1 mins or so, it's pointless to query the db every time we get a ping. diff --git a/packages/server/modules/shared/roles.js b/packages/server/modules/shared/roles.js new file mode 100644 index 000000000..4cf2f10f5 --- /dev/null +++ b/packages/server/modules/shared/roles.js @@ -0,0 +1,12 @@ +const knex = require(`@/db/knex`) +let roles + +const getRoles = async () => { + if (roles) return roles + roles = await knex('user_roles').select('*') + return roles +} + +module.exports = { + getRoles +} From 105f1c0b545cfded0ac58f3d4b80b9b3fd0ca242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 1 Aug 2023 14:58:55 +0200 Subject: [PATCH 41/43] refactor(server authz): rework validateServerRole --- .../modules/core/graph/directives/hasRole.js | 8 +++-- .../modules/core/graph/resolvers/objects.js | 9 ++--- .../modules/core/graph/resolvers/projects.ts | 5 +-- .../modules/core/graph/resolvers/server.js | 5 +-- .../modules/core/graph/resolvers/streams.js | 9 ++--- .../modules/core/graph/resolvers/users.js | 17 +++++----- .../server/modules/core/rest/authUtils.js | 11 +++--- .../core/tests/favoriteStreams.spec.js | 2 +- .../server/modules/core/tests/generic.spec.js | 26 ++++++++------ packages/server/modules/shared/authz.ts | 29 ++++++++++------ .../server/modules/shared/errors/index.ts | 2 +- packages/server/modules/shared/index.js | 34 ++++++++----------- .../server/modules/shared/test/authz.spec.js | 20 +++++++---- .../modules/stats/graph/resolvers/stats.js | 5 +-- 14 files changed, 98 insertions(+), 84 deletions(-) diff --git a/packages/server/modules/core/graph/directives/hasRole.js b/packages/server/modules/core/graph/directives/hasRole.js index 681aae2c6..eb250648b 100644 --- a/packages/server/modules/core/graph/directives/hasRole.js +++ b/packages/server/modules/core/graph/directives/hasRole.js @@ -1,11 +1,12 @@ const { defaultFieldResolver } = require('graphql') -const { validateServerRole, authorizeResolver } = require('@/modules/shared') +const { authorizeResolver } = require('@/modules/shared') const { ForbiddenError } = require('@/modules/shared/errors') const { mapSchema, getDirective, MapperKind } = require('@graphql-tools/utils') const { mapStreamRoleToValue, mapServerRoleToValue } = require('@/modules/core/helpers/graphTypes') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') module.exports = { /** @@ -38,7 +39,10 @@ module.exports = { const { resolve = defaultFieldResolver } = fieldConfig fieldConfig.resolve = async function (...args) { const context = args[2] - await validateServerRole(context, mapServerRoleToValue(requiredRole)) + await throwForNotHavingServerRole( + context, + mapServerRoleToValue(requiredRole) + ) return await resolve.apply(this, args) } diff --git a/packages/server/modules/core/graph/resolvers/objects.js b/packages/server/modules/core/graph/resolvers/objects.js index bc9b28901..3019b02b4 100644 --- a/packages/server/modules/core/graph/resolvers/objects.js +++ b/packages/server/modules/core/graph/resolvers/objects.js @@ -1,9 +1,5 @@ 'use strict' -const { - validateServerRole, - validateScopes, - authorizeResolver -} = require('@/modules/shared') +const { validateScopes, authorizeResolver } = require('@/modules/shared') const { createObjects, @@ -12,6 +8,7 @@ const { getObjectChildrenQuery } = require('../../services/objects') const { Roles, Scopes } = require('@speckle/shared') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') module.exports = { Stream: { @@ -60,7 +57,7 @@ module.exports = { }, Mutation: { async objectCreate(parent, args, context) { - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Streams.Write) await authorizeResolver( context.userId, diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index acefd66f0..2bffad5ff 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -30,7 +30,8 @@ import { createStreamInviteAndNotify, useStreamInviteAndNotify } from '@/modules/serverinvites/services/management' -import { authorizeResolver, validateScopes, validateServerRole } from '@/modules/shared' +import { authorizeResolver, validateScopes } from '@/modules/shared' +import { throwForNotHavingServerRole } from '@/modules/shared/authz' import { filteredSubscribe, ProjectSubscriptions, @@ -52,7 +53,7 @@ export = { await authorizeResolver(context.userId, args.id, Roles.Stream.Reviewer) if (!stream.isPublic) { - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) validateScopes(context.scopes, Scopes.Streams.Read) } diff --git a/packages/server/modules/core/graph/resolvers/server.js b/packages/server/modules/core/graph/resolvers/server.js index e9fbffa38..abe6ff44d 100644 --- a/packages/server/modules/core/graph/resolvers/server.js +++ b/packages/server/modules/core/graph/resolvers/server.js @@ -1,5 +1,5 @@ 'use strict' -const { validateServerRole, validateScopes } = require('@/modules/shared') +const { validateScopes } = require('@/modules/shared') const { updateServerInfo, getServerInfo, @@ -7,6 +7,7 @@ const { getPublicRoles } = require('../../services/generic') const { Roles, Scopes } = require('@speckle/shared') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') module.exports = { Query: { @@ -27,7 +28,7 @@ module.exports = { Mutation: { async serverInfoUpdate(parent, args, context) { - await validateServerRole(context, Roles.Server.Admin) + await throwForNotHavingServerRole(context, Roles.Server.Admin) await validateScopes(context.scopes, Scopes.Server.Setup) await updateServerInfo(args.info) diff --git a/packages/server/modules/core/graph/resolvers/streams.js b/packages/server/modules/core/graph/resolvers/streams.js index 69dad153e..2ba50f281 100644 --- a/packages/server/modules/core/graph/resolvers/streams.js +++ b/packages/server/modules/core/graph/resolvers/streams.js @@ -18,11 +18,7 @@ const { StreamSubscriptions: StreamPubsubEvents } = require(`@/modules/shared/utils/subscriptions`) -const { - authorizeResolver, - validateScopes, - validateServerRole -} = require(`@/modules/shared`) +const { authorizeResolver, validateScopes } = require(`@/modules/shared`) const { RateLimitError, RateLimitAction, @@ -53,6 +49,7 @@ const { const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') const { Roles, Scopes } = require('@speckle/shared') const { StreamNotFoundError } = require('@/modules/core/errors/stream') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') // subscription events const USER_STREAM_ADDED = StreamPubsubEvents.UserStreamAdded @@ -91,7 +88,7 @@ module.exports = { await authorizeResolver(context.userId, args.id, Roles.Stream.Reviewer) if (!stream.isPublic) { - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Streams.Read) } diff --git a/packages/server/modules/core/graph/resolvers/users.js b/packages/server/modules/core/graph/resolvers/users.js index 04e3e8556..87dd2e5d4 100644 --- a/packages/server/modules/core/graph/resolvers/users.js +++ b/packages/server/modules/core/graph/resolvers/users.js @@ -11,7 +11,7 @@ const { const { updateUserAndNotify } = require('@/modules/core/services/users/management') const { saveActivity } = require('@/modules/activitystream/services') const { ActionTypes } = require('@/modules/activitystream/helpers/types') -const { validateServerRole, validateScopes } = require(`@/modules/shared`) +const { validateScopes } = require(`@/modules/shared`) const zxcvbn = require('zxcvbn') const { getAdminUsersListCollection @@ -20,6 +20,7 @@ const { Roles, Scopes } = require('@speckle/shared') const { markOnboardingComplete } = require('@/modules/core/repositories/users') const { UsersMeta } = require('@/modules/core/dbSchema') const { getServerInfo } = require('@/modules/core/services/generic') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') /** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */ module.exports = { @@ -32,7 +33,7 @@ module.exports = { if (!activeUserId) return null // Only if authenticated - check for server roles & scopes - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Read) return await getUser(activeUserId) @@ -46,7 +47,7 @@ module.exports = { // User wants info about himself and he's not authenticated - just return null if (!context.auth && !args.id) return null - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) if (!args.id) await validateScopes(context.scopes, Scopes.Profile.Read) else await validateScopes(context.scopes, Scopes.Users.Read) @@ -63,7 +64,7 @@ module.exports = { }, async userSearch(parent, args, context) { - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Read) await validateScopes(context.scopes, Scopes.Users.Read) @@ -105,7 +106,7 @@ module.exports = { try { // you should only have access to other users email if you have elevated privileges - await validateServerRole(context, Roles.Server.Admin) + await throwForNotHavingServerRole(context, Roles.Server.Admin) await validateScopes(context.scopes, Scopes.Users.Email) return parent.email } catch (err) { @@ -130,7 +131,7 @@ module.exports = { }, Mutation: { async userUpdate(_parent, args, context) { - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await updateUserAndNotify(context.userId, args.user) return true }, @@ -146,7 +147,7 @@ module.exports = { }, async adminDeleteUser(_parent, args, context) { - await validateServerRole(context, Roles.Server.Admin) + await throwForNotHavingServerRole(context, Roles.Server.Admin) const user = await getUserByEmail({ email: args.userConfirmation.email }) await deleteUser(user.id) return true @@ -162,7 +163,7 @@ module.exports = { // The below are not really needed anymore as we've added the hasRole and hasScope // directives in the graphql schema itself. // Since I am paranoid, I'll leave them here too. - await validateServerRole(context, Roles.Server.Guest) + await throwForNotHavingServerRole(context, Roles.Server.Guest) await validateScopes(context.scopes, Scopes.Profile.Delete) await deleteUser(context.userId, args.user) diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index fbb80ad75..d9895c060 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -1,12 +1,9 @@ 'use strict' -const { - validateScopes, - validateServerRole, - authorizeResolver -} = require('@/modules/shared') +const { validateScopes, authorizeResolver } = require('@/modules/shared') const { getStream } = require('../services/streams') const { Roles, Scopes } = require('@speckle/shared') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') module.exports = { async validatePermissionsReadStream(streamId, req) { @@ -14,7 +11,7 @@ module.exports = { if (stream?.isPublic) return { result: true, status: 200 } try { - await validateServerRole(req.context, Roles.Server.Guest) + await throwForNotHavingServerRole(req.context, Roles.Server.Guest) } catch (err) { return { result: false, status: 401 } } @@ -47,7 +44,7 @@ module.exports = { } try { - await validateServerRole(req.context, Roles.Server.Guest) + await throwForNotHavingServerRole(req.context, Roles.Server.Guest) } catch (err) { return { result: false, status: 401 } } diff --git a/packages/server/modules/core/tests/favoriteStreams.spec.js b/packages/server/modules/core/tests/favoriteStreams.spec.js index dba6f6612..abb3680a5 100644 --- a/packages/server/modules/core/tests/favoriteStreams.spec.js +++ b/packages/server/modules/core/tests/favoriteStreams.spec.js @@ -373,7 +373,7 @@ describe('Favorite streams', () => { expect(result.data.streamFavorite).to.not.be.ok expect(result.errors).to.have.lengthOf(1) - expect(result.errors.at(0).message).to.contain('must provide an auth token') + expect(result.errors.at(0).message).to.contain('Must provide an auth token') }) it("can't be retrieved", async () => { diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index 39160f278..838c4af1e 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -13,14 +13,11 @@ const { beforeEachContext } = require('@/test/hooks') const { createStream } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') -const { - validateServerRole, - validateScopes, - authorizeResolver -} = require('@/modules/shared') +const { validateScopes, authorizeResolver } = require('@/modules/shared') const { buildContext } = require('@/modules/shared/middleware') const { ForbiddenError } = require('apollo-server-express') const { Roles, Scopes } = require('@speckle/shared') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') describe('Generic AuthN & AuthZ controller tests', () => { before(async () => { @@ -61,7 +58,7 @@ describe('Generic AuthN & AuthZ controller tests', () => { ) it('Should validate server role', async () => { - await validateServerRole( + await throwForNotHavingServerRole( { auth: true, role: Roles.Server.User }, Roles.Server.Admin ) @@ -72,19 +69,26 @@ describe('Generic AuthN & AuthZ controller tests', () => { expect('You do not have the required server role').to.equal(err.message) ) - await validateServerRole({ auth: true, role: 'HACZOR' }, '133TCR3w') + await throwForNotHavingServerRole({ auth: true, role: 'HACZOR' }, '133TCR3w') .then(() => { throw new Error('This should have been rejected') }) - .catch((err) => expect('Invalid server role specified').to.equal(err.message)) + .catch((err) => + expect('Invalid role requirement specified').to.equal(err.message) + ) - await validateServerRole({ auth: true, role: Roles.Server.Admin }, '133TCR3w') + await throwForNotHavingServerRole( + { auth: true, role: Roles.Server.Admin }, + '133TCR3w' + ) .then(() => { throw new Error('This should have been rejected') }) - .catch((err) => expect('Invalid server role specified').to.equal(err.message)) + .catch((err) => + expect('Invalid role requirement specified').to.equal(err.message) + ) - const test = await validateServerRole( + const test = await throwForNotHavingServerRole( { auth: true, role: Roles.Server.Admin }, Roles.Server.User ) diff --git a/packages/server/modules/shared/authz.ts b/packages/server/modules/shared/authz.ts index 7a217934c..89a5897d9 100644 --- a/packages/server/modules/shared/authz.ts +++ b/packages/server/modules/shared/authz.ts @@ -114,17 +114,13 @@ export function validateRole({ // role validation has nothing to do with auth... //this check doesn't belong here, move it out to the auth pipeline if (!context.auth) - return authFailed( - context, - new UnauthorizedError('Cannot validate role without auth') - ) + return authFailed(context, new UnauthorizedError('Must provide an auth token')) const contextRole = roleGetter(context) - if (!contextRole) - return authFailed( - context, - new ForbiddenError('You do not have the required role') - ) + const missingRoleMessage = `You do not have the required ${ + requiredRole.split(':')[0] + } role` + if (!contextRole) return authFailed(context, new ForbiddenError(missingRoleMessage)) const role = roles.find((r) => r.name === requiredRole) const myRole = roles.find((r) => r.name === contextRole) @@ -138,7 +134,7 @@ export function validateRole({ return authFailed(context, new ForbiddenError('Your role is not valid')) if (myRole.name === iddqd || myRole.weight >= role.weight) return authSuccess(context) - return authFailed(context, new ForbiddenError('You do not have the required role')) + return authFailed(context, new ForbiddenError(missingRoleMessage)) } } @@ -277,3 +273,16 @@ export const streamReadPermissions = [ ] if (adminOverrideEnabled()) streamReadPermissions.push(allowForServerAdmins) + +export const throwForNotHavingServerRole = async ( + context: AuthContext, + requiredRole: ServerRoles +) => { + const { authResult } = await validateServerRole({ requiredRole })({ + context, + authResult: { authorized: false } + }) + if (authHasFailed(authResult)) + throw authResult.error ?? new Error('Auth failed without an error') + return true +} diff --git a/packages/server/modules/shared/errors/index.ts b/packages/server/modules/shared/errors/index.ts index 82f0507e2..43e653319 100644 --- a/packages/server/modules/shared/errors/index.ts +++ b/packages/server/modules/shared/errors/index.ts @@ -1,7 +1,7 @@ import { BaseError } from '@/modules/shared/errors/base' export class ForbiddenError extends BaseError { - static code = 'FORBIDDEN_ERROR' + static code = 'FORBIDDEN' static defaultMessage = 'Access to the resource is forbidden' } diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index 9aa32777f..5c5908bfa 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -8,29 +8,25 @@ const { BranchSubscriptions } = require('@/modules/shared/utils/subscriptions') const { Roles } = require('@speckle/shared') -const { - validateServerRole: authPipelineValidateServerRole, - authHasFailed -} = require('@/modules/shared/authz') const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') const { ServerAcl: ServerAclSchema } = require('@/modules/core/dbSchema') const { getRoles } = require('@/modules/shared/roles') const ServerAcl = () => ServerAclSchema.knex() -/** - * Validates a server role against the req's context object. - * @param {import('@/modules/shared/helpers/typeHelper').GraphQLContext} context - * @param {string} requiredRole - */ -async function validateServerRole(context, requiredRole) { - const { authResult } = await authPipelineValidateServerRole({ requiredRole })({ - context - }) - if (authHasFailed(authResult)) - throw authResult.error ?? new Error('Auth failed without an error') - return true -} +// /** +// * Validates a server role against the req's context object. +// * @param {import('@/modules/shared/helpers/typeHelper').GraphQLContext} context +// * @param {string} requiredRole +// */ +// async function validateServerRole(context, requiredRole) { +// const { authResult } = await authPipelineValidateServerRole({ requiredRole })({ +// context +// }) +// if (authHasFailed(authResult)) +// throw authResult.error ?? new Error('Auth failed without an error') +// return true +// } /** * Validates the scope against a list of scopes of the current session. @@ -53,7 +49,7 @@ async function validateScopes(scopes, scope) { async function authorizeResolver(userId, resourceId, requiredRole) { userId = userId || null - const roles = getRoles() + const roles = await getRoles() // TODO: Cache these results with a TTL of 1 mins or so, it's pointless to query the db every time we get a ping. @@ -117,7 +113,7 @@ async function registerOrUpdateRole(role) { module.exports = { registerOrUpdateScope, registerOrUpdateRole, - validateServerRole, + // validateServerRole, validateScopes, authorizeResolver, pubsub, diff --git a/packages/server/modules/shared/test/authz.spec.js b/packages/server/modules/shared/test/authz.spec.js index 8b1d06e83..829ef0ed0 100644 --- a/packages/server/modules/shared/test/authz.spec.js +++ b/packages/server/modules/shared/test/authz.spec.js @@ -66,7 +66,7 @@ describe('AuthZ @shared', () => { describe('Role validation', () => { const rolesLookup = async () => [ { name: '1', weight: 1 }, - { name: '2', weight: 2 }, + { name: 'server:2', weight: 2 }, { name: '3', weight: 3 }, { name: 'goku', weight: 9001 }, { name: '42', weight: 42 } @@ -75,15 +75,18 @@ describe('AuthZ @shared', () => { const testData = [ { name: 'Having lower privileged role than required results auth failed', - requiredRole: '2', + requiredRole: 'server:2', context: { auth: true, role: '1' }, - expectedResult: authFailed(null, new SFE('You do not have the required role')) + expectedResult: authFailed( + null, + new SFE('You do not have the required server role') + ) }, { name: 'Not having auth fails role validation', - requiredRole: '2', + requiredRole: 'server:2', context: { auth: false }, - expectedResult: authFailed(null, new SUE('Cannot validate role without auth')) + expectedResult: authFailed(null, new SUE('Must provide an auth token')) }, { name: 'Requiring a junk role fails auth', @@ -93,7 +96,7 @@ describe('AuthZ @shared', () => { }, { name: 'Having a junk role fails auth', - requiredRole: '2', + requiredRole: 'server:2', context: { auth: true, role: 'iddqd' }, expectedResult: authFailed(null, new SFE('Your role is not valid')) }, @@ -101,7 +104,10 @@ describe('AuthZ @shared', () => { name: 'Not having the required level fails', requiredRole: 'goku', context: { auth: true, role: '3' }, - expectedResult: authFailed(null, new SFE('You do not have the required role')) + expectedResult: authFailed( + null, + new SFE('You do not have the required goku role') + ) }, { name: 'Having the god mode role defeats even higher privilege requirement', diff --git a/packages/server/modules/stats/graph/resolvers/stats.js b/packages/server/modules/stats/graph/resolvers/stats.js index 7ac09896f..ca2cf8895 100644 --- a/packages/server/modules/stats/graph/resolvers/stats.js +++ b/packages/server/modules/stats/graph/resolvers/stats.js @@ -1,5 +1,5 @@ 'use strict' -const { validateServerRole, validateScopes } = require('@/modules/shared') +const { validateScopes } = require('@/modules/shared') const { getStreamHistory, getCommitHistory, @@ -11,11 +11,12 @@ const { getTotalUserCount } = require('../../services') const { Roles, Scopes } = require('@speckle/shared') +const { throwForNotHavingServerRole } = require('@/modules/shared/authz') module.exports = { Query: { async serverStats(parent, args, context) { - await validateServerRole(context, Roles.Server.Admin) + await throwForNotHavingServerRole(context, Roles.Server.Admin) await validateScopes(context.scopes, Scopes.Server.Stats) return {} } From 296ecc83402a2e2a56716d3469867978a8e755cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 1 Aug 2023 15:03:19 +0200 Subject: [PATCH 42/43] chore(server): remove dead comment --- packages/server/modules/shared/index.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/server/modules/shared/index.js b/packages/server/modules/shared/index.js index 5c5908bfa..dfda2a8ba 100644 --- a/packages/server/modules/shared/index.js +++ b/packages/server/modules/shared/index.js @@ -14,20 +14,6 @@ const { ServerAcl: ServerAclSchema } = require('@/modules/core/dbSchema') const { getRoles } = require('@/modules/shared/roles') const ServerAcl = () => ServerAclSchema.knex() -// /** -// * Validates a server role against the req's context object. -// * @param {import('@/modules/shared/helpers/typeHelper').GraphQLContext} context -// * @param {string} requiredRole -// */ -// async function validateServerRole(context, requiredRole) { -// const { authResult } = await authPipelineValidateServerRole({ requiredRole })({ -// context -// }) -// if (authHasFailed(authResult)) -// throw authResult.error ?? new Error('Auth failed without an error') -// return true -// } - /** * Validates the scope against a list of scopes of the current session. * @param {string[]|undefined} scopes From 1a46f1fee3f5d26fdefd42743de5329d99b856e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 1 Aug 2023 16:03:10 +0200 Subject: [PATCH 43/43] feat(frontend1): enable guest role mapping in user admin --- packages/frontend/src/main/pages/admin/Users.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/main/pages/admin/Users.vue b/packages/frontend/src/main/pages/admin/Users.vue index c6c7e252d..fdee0cca6 100644 --- a/packages/frontend/src/main/pages/admin/Users.vue +++ b/packages/frontend/src/main/pages/admin/Users.vue @@ -143,7 +143,8 @@ export default { roleLookupTable: { [Roles.Server.User]: 'User', [Roles.Server.Admin]: 'Admin', - [Roles.Server.ArchivedUser]: 'Archived' + [Roles.Server.ArchivedUser]: 'Archived', + [Roles.Server.Guest]: 'Guest' }, adminUsers: { items: [],