{"action":"closed","number":1384,"pull_request":{"url":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384","id":77739695,"html_url":"https://github.com/umbraco/Umbraco-CMS/pull/1384","diff_url":"https://github.com/umbraco/Umbraco-CMS/pull/1384.diff","patch_url":"https://github.com/umbraco/Umbraco-CMS/pull/1384.patch","issue_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/1384","number":1384,"state":"closed","locked":false,"title":"U4-8632 - Back-office user groups","user":{"login":"AndyButland","id":1993459,"avatar_url":"https://avatars.githubusercontent.com/u/1993459?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyButland","html_url":"https://github.com/AndyButland","followers_url":"https://api.github.com/users/AndyButland/followers","following_url":"https://api.github.com/users/AndyButland/following{/other_user}","gists_url":"https://api.github.com/users/AndyButland/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyButland/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyButland/subscriptions","organizations_url":"https://api.github.com/users/AndyButland/orgs","repos_url":"https://api.github.com/users/AndyButland/repos","events_url":"https://api.github.com/users/AndyButland/events{/privacy}","received_events_url":"https://api.github.com/users/AndyButland/received_events","type":"User","site_admin":false},"body":"This PR consists of an implementation for a feature to allow the setting of back-office permissions via groups of users, rather than users individually.  As per [some notes in the related issue](http://issues.umbraco.org/issue/U4-8632#comment=67-29853), I've implemented this in a way that seems to me simplest and least intrusive in terms of code changes and issues around migration of existing sites.  In particular I've retained the existing features for setting and checking permissions on users and user types, and introduced groups as well - rather that looking to replace types with groups.  The end result is probably not quite as you'd design such a permissions system from scratch, but is likely an easier way to introduce this feature to the current code base.\r\n\r\nIn practice I'd expect people to use either the existing setup (user types and user permissions), or use the groups and not use individual user permissions except for odd exceptions.\r\n\r\nTo set up some tables are required.  The PR contains a (perhaps optimistic!) migration for 7.6, but to set up for testing it's probably easiest to run the following SQL script:\r\n\r\n```\r\n/* umbracoUserGroup - user groups */\r\nCREATE TABLE [dbo].[umbracoUserGroup](\r\n\t[id] [int] IDENTITY(1,1) NOT NULL,\r\n\t[userGroupAlias] [nvarchar](200) NULL,\r\n\t[userGroupName] [nvarchar](200) NOT NULL,\r\n\t[userGroupDefaultPermissions] [nvarchar](50) NULL,\r\n CONSTRAINT [PK_umbracoUserGroup] PRIMARY KEY CLUSTERED \r\n(\r\n\t[id] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\n) ON [PRIMARY]\r\nGO\r\n\r\n/* umbracoUser2UserGroup - maps users to user groups */\r\nCREATE TABLE [dbo].[umbracoUser2UserGroup](\r\n\t[userId] [int] NOT NULL,\r\n\t[userGroupId] [int] NOT NULL\r\n CONSTRAINT [PK_user2userGroup] PRIMARY KEY CLUSTERED \r\n(\r\n\t[userId] ASC,\r\n\t[userGroupId] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\n) ON [PRIMARY]\r\nGO\r\nALTER TABLE [dbo].[umbracoUser2UserGroup]  WITH CHECK ADD  CONSTRAINT [FK_umbracoUser2UserGroup_userId] FOREIGN KEY([userId])\r\nREFERENCES [dbo].[umbracoUser] ([id])\r\nGO\r\nALTER TABLE [dbo].[umbracoUser2UserGroup]  WITH CHECK ADD  CONSTRAINT [FK_umbracoUser2UserGroup_userGroupId] FOREIGN KEY([userGroupId])\r\nREFERENCES [dbo].[umbracoUserGroup] ([id])\r\nGO\r\n\r\n/* umbracoUserGroup2App - maps user groups to apps */\r\nCREATE TABLE [dbo].[umbracoUserGroup2App](\r\n\t[userGroupId] [int] NOT NULL,\r\n\t[app] [nvarchar](50) NOT NULL,\r\n CONSTRAINT [PK_userGroup2App] PRIMARY KEY CLUSTERED \r\n(\r\n\t[userGroupId] ASC,\r\n\t[app] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\n) ON [PRIMARY]\r\n\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2App]  WITH CHECK ADD  CONSTRAINT [FK_umbracoUserGroup2App_umbracoGroupUser_id] FOREIGN KEY([userGroupId])\r\nREFERENCES [dbo].[umbracoUserGroup] ([id])\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2App] CHECK CONSTRAINT [FK_umbracoUserGroup2App_umbracoGroupUser_id]\r\nGO\r\n\r\n/* umbracoUserGroup2NodePermission - maps user groups to node permissions */\r\nCREATE TABLE [dbo].[umbracoUserGroup2NodePermission] (\r\n\t[userGroupId] [int] NOT NULL,\r\n\t[nodeId] [int] NOT NULL,\r\n\t[permission] [nvarchar](255) NOT NULL,\r\n CONSTRAINT [PK_umbracoUserGroup2NodePermission] PRIMARY KEY CLUSTERED \r\n(\r\n\t[userGroupId] ASC,\r\n\t[nodeId] ASC,\r\n\t[permission] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\n) ON [PRIMARY]\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2NodePermission]  WITH CHECK ADD  CONSTRAINT [FK_umbracoUserGroup2NodePermission_umbracoNode_id] FOREIGN KEY([nodeId])\r\nREFERENCES [dbo].[umbracoNode] ([id])\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2NodePermission] CHECK CONSTRAINT [FK_umbracoUserGroup2NodePermission_umbracoNode_id]\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2NodePermission]  WITH CHECK ADD  CONSTRAINT [FK_umbracoUserGroup2NodePermission_umbracoUser_id] FOREIGN KEY([userGroupId])\r\nREFERENCES [dbo].[umbracoUserGroup] ([id])\r\nGO\r\nALTER TABLE [dbo].[umbracoUserGroup2NodePermission] CHECK CONSTRAINT [FK_umbracoUserGroup2NodePermission_umbracoUser_id]\r\nGO\r\n```\r\n\r\nOnce installed you should find with this PR:\r\n\r\n- Back-office management screens for creating groups and adding users to them\r\n- Amended back-office management screen for users to allow assigning them to groups\r\n- An additional back-office management screen for setting node permissions to a group (similar to that already there for setting them on a user)\r\n- Amends to code that retrieves a user to also pull back the groups they are associated with\r\n- Checks for access to sections that takes account of groups:\r\n     - A user will be allowed access to any section they have access to as per their user permissions, PLUS any they have access to be being part of a group that has been given that permission \r\n- Checks for node action permissions that takes account of groups:\r\n     - A user with permissions directly assigned to them for a node will use those and not look any further.  If there are none, the permissions the user receives will be the most permissive set of all actions allowed for their user type and the groups they are in.  E.g. if a user has a type that can do \"A\", one group that can do \"B\" and another that can do \"C\", then they should be able to do \"A\", \"B\" and \"C\"\r\n\r\nCode-wise, at least in the UI layer, there's a bit of legacy code that has been dug into here that I imagine is due to be looked at again anyway for V8.  I've not attempted to update that, but have where possible moved code to shared base classes where similar functionality is required for existing user permissions and the new group permissions.  So although there's code in here that will likely be updated in the not too distant future, I've tried not to add too much to the legacy code and avoided duplicating where I can.\r\n\r\nOtherwise whilst there's a fair bit here a lot is analogous to existing code that already handled users or user types, so other than the logic around determining permissions where we have introduced groups, not too much actually brand new.","created_at":"2016-07-17T16:37:10Z","updated_at":"2016-10-30T11:13:58Z","closed_at":"2016-10-30T11:13:58Z","merged_at":null,"merge_commit_sha":"a35631afcf45abd2be4ab135c60a6d61a175999d","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384/commits","review_comments_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384/comments","review_comment_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/comments{/number}","comments_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/1384/comments","statuses_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/statuses/e04f8192f31d1bd462d04e352c14c6b853971bb0","head":{"label":"AndyButland:u4-8632","ref":"u4-8632","sha":"e04f8192f31d1bd462d04e352c14c6b853971bb0","user":{"login":"AndyButland","id":1993459,"avatar_url":"https://avatars.githubusercontent.com/u/1993459?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyButland","html_url":"https://github.com/AndyButland","followers_url":"https://api.github.com/users/AndyButland/followers","following_url":"https://api.github.com/users/AndyButland/following{/other_user}","gists_url":"https://api.github.com/users/AndyButland/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyButland/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyButland/subscriptions","organizations_url":"https://api.github.com/users/AndyButland/orgs","repos_url":"https://api.github.com/users/AndyButland/repos","events_url":"https://api.github.com/users/AndyButland/events{/privacy}","received_events_url":"https://api.github.com/users/AndyButland/received_events","type":"User","site_admin":false},"repo":{"id":13280163,"name":"Umbraco-CMS","full_name":"AndyButland/Umbraco-CMS","owner":{"login":"AndyButland","id":1993459,"avatar_url":"https://avatars.githubusercontent.com/u/1993459?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyButland","html_url":"https://github.com/AndyButland","followers_url":"https://api.github.com/users/AndyButland/followers","following_url":"https://api.github.com/users/AndyButland/following{/other_user}","gists_url":"https://api.github.com/users/AndyButland/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyButland/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyButland/subscriptions","organizations_url":"https://api.github.com/users/AndyButland/orgs","repos_url":"https://api.github.com/users/AndyButland/repos","events_url":"https://api.github.com/users/AndyButland/events{/privacy}","received_events_url":"https://api.github.com/users/AndyButland/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/AndyButland/Umbraco-CMS","description":"The simple, flexible and friendly ASP.NET CMS used by more than 175.000 websites","fork":true,"url":"https://api.github.com/repos/AndyButland/Umbraco-CMS","forks_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/forks","keys_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/teams","hooks_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/hooks","issue_events_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/issues/events{/number}","events_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/events","assignees_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/assignees{/user}","branches_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/branches{/branch}","tags_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/tags","blobs_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/git/refs{/sha}","trees_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/statuses/{sha}","languages_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/languages","stargazers_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/stargazers","contributors_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/contributors","subscribers_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/subscribers","subscription_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/subscription","commits_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/commits{/sha}","git_commits_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/git/commits{/sha}","comments_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/comments{/number}","issue_comment_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/issues/comments{/number}","contents_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/contents/{+path}","compare_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/merges","archive_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/downloads","issues_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/issues{/number}","pulls_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/pulls{/number}","milestones_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/milestones{/number}","notifications_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/labels{/name}","releases_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/releases{/id}","deployments_url":"https://api.github.com/repos/AndyButland/Umbraco-CMS/deployments","created_at":"2013-10-02T19:05:55Z","updated_at":"2014-04-18T22:23:20Z","pushed_at":"2016-10-30T10:58:48Z","git_url":"git://github.com/AndyButland/Umbraco-CMS.git","ssh_url":"git@github.com:AndyButland/Umbraco-CMS.git","clone_url":"https://github.com/AndyButland/Umbraco-CMS.git","svn_url":"https://github.com/AndyButland/Umbraco-CMS","homepage":"http://umbraco.com","size":121260,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"6.2.0"}},"base":{"label":"umbraco:dev-v7","ref":"dev-v7","sha":"a758688c52274aa5c62286bd701febebcc3d2c06","user":{"login":"umbraco","id":1419552,"avatar_url":"https://avatars.githubusercontent.com/u/1419552?v=3","gravatar_id":"","url":"https://api.github.com/users/umbraco","html_url":"https://github.com/umbraco","followers_url":"https://api.github.com/users/umbraco/followers","following_url":"https://api.github.com/users/umbraco/following{/other_user}","gists_url":"https://api.github.com/users/umbraco/gists{/gist_id}","starred_url":"https://api.github.com/users/umbraco/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/umbraco/subscriptions","organizations_url":"https://api.github.com/users/umbraco/orgs","repos_url":"https://api.github.com/users/umbraco/repos","events_url":"https://api.github.com/users/umbraco/events{/privacy}","received_events_url":"https://api.github.com/users/umbraco/received_events","type":"Organization","site_admin":false},"repo":{"id":10601208,"name":"Umbraco-CMS","full_name":"umbraco/Umbraco-CMS","owner":{"login":"umbraco","id":1419552,"avatar_url":"https://avatars.githubusercontent.com/u/1419552?v=3","gravatar_id":"","url":"https://api.github.com/users/umbraco","html_url":"https://github.com/umbraco","followers_url":"https://api.github.com/users/umbraco/followers","following_url":"https://api.github.com/users/umbraco/following{/other_user}","gists_url":"https://api.github.com/users/umbraco/gists{/gist_id}","starred_url":"https://api.github.com/users/umbraco/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/umbraco/subscriptions","organizations_url":"https://api.github.com/users/umbraco/orgs","repos_url":"https://api.github.com/users/umbraco/repos","events_url":"https://api.github.com/users/umbraco/events{/privacy}","received_events_url":"https://api.github.com/users/umbraco/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/umbraco/Umbraco-CMS","description":"The simple, flexible and friendly ASP.NET CMS used by more than 360.000 websites","fork":false,"url":"https://api.github.com/repos/umbraco/Umbraco-CMS","forks_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/forks","keys_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/teams","hooks_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/hooks","issue_events_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/events{/number}","events_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/events","assignees_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/assignees{/user}","branches_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/branches{/branch}","tags_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/tags","blobs_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/git/refs{/sha}","trees_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/statuses/{sha}","languages_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/languages","stargazers_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/stargazers","contributors_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/contributors","subscribers_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/subscribers","subscription_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/subscription","commits_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/commits{/sha}","git_commits_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/git/commits{/sha}","comments_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/comments{/number}","issue_comment_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/comments{/number}","contents_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/contents/{+path}","compare_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/merges","archive_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/downloads","issues_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues{/number}","pulls_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls{/number}","milestones_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/milestones{/number}","notifications_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/labels{/name}","releases_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/releases{/id}","deployments_url":"https://api.github.com/repos/umbraco/Umbraco-CMS/deployments","created_at":"2013-06-10T13:50:46Z","updated_at":"2016-10-29T05:22:36Z","pushed_at":"2016-10-30T11:13:30Z","git_url":"git://github.com/umbraco/Umbraco-CMS.git","ssh_url":"git@github.com:umbraco/Umbraco-CMS.git","clone_url":"https://github.com/umbraco/Umbraco-CMS.git","svn_url":"https://github.com/umbraco/Umbraco-CMS","homepage":"http://umbraco.com","size":200056,"stargazers_count":1380,"watchers_count":1380,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1108,"mirror_url":null,"open_issues_count":118,"forks":1108,"open_issues":118,"watchers":1380,"default_branch":"dev-v7"}},"_links":{"self":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384"},"html":{"href":"https://github.com/umbraco/Umbraco-CMS/pull/1384"},"issue":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/1384"},"comments":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/issues/1384/comments"},"review_comments":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384/comments"},"review_comment":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/pulls/1384/commits"},"statuses":{"href":"https://api.github.com/repos/umbraco/Umbraco-CMS/statuses/e04f8192f31d1bd462d04e352c14c6b853971bb0"}},"merged":false,"mergeable":false,"mergeable_state":"dirty","merged_by":null,"comments":6,"review_comments":0,"commits":1,"additions":5040,"deletions":1009,"changed_files":119}}
{"actor":{"display_login":"AndyButland"}}