Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP][JENKINS-58293] Folder based Authorization as a new AuthorizationStrategy #89

Closed
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7ea9bad
[WIP] Folder based Authorization as a new AuthorizationStrategy
AbhyudayaSharma Jul 2, 2019
a02ea97
Add GlobalRole implementation for folders
AbhyudayaSharma Jul 3, 2019
15d7222
Fix GlobalAclImpl's behaviour and add unit test for it
AbhyudayaSharma Jul 3, 2019
82de631
Add a basic Jelly view to add global role from browser
AbhyudayaSharma Jul 3, 2019
b70972a
Adding global roles now works from the Web UI
AbhyudayaSharma Jul 4, 2019
1b35cd1
Cleanup code
AbhyudayaSharma Jul 4, 2019
b3e97b2
Fix Findbugs warnings and improve the UI
AbhyudayaSharma Jul 4, 2019
5043e0c
Allow assigning sids to global roles from web UI
AbhyudayaSharma Jul 5, 2019
0abbc87
Add a wrapper for Permission to make config.xml smaller
AbhyudayaSharma Jul 5, 2019
cdd82e1
Git ignore .factorypath
AbhyudayaSharma Jul 5, 2019
cee8c32
Fix GlobalAclImplTest and add utility method to wrap up Permissions
AbhyudayaSharma Jul 5, 2019
4a61f8b
Implement basic folder authorization
AbhyudayaSharma Jul 7, 2019
e9babe7
Allow adding FolderRoles from web UI
AbhyudayaSharma Jul 8, 2019
e3bdf1f
Fix JavaDoc
AbhyudayaSharma Jul 8, 2019
a518384
Allow assigning FolderRoles to Sids from Web UI
AbhyudayaSharma Jul 8, 2019
c11e7ae
Add a unit test for FolderBasedAuthorizationStrategy
AbhyudayaSharma Jul 8, 2019
359ef9f
Use JavaScript to submit 'Add folder role' form
AbhyudayaSharma Jul 8, 2019
a5deb46
Make Jelly UI look less bad
AbhyudayaSharma Jul 8, 2019
2853ca5
Whitelist `java.util.concurrent.ConcurrentHashMap$KeySetView`
AbhyudayaSharma Jul 9, 2019
20f6657
Allow deleting global and folder roles
AbhyudayaSharma Jul 9, 2019
5fe72ef
Add administrator checks to deleting roles
AbhyudayaSharma Jul 9, 2019
7e18738
Cache ACLs that contain inheritance data
AbhyudayaSharma Jul 9, 2019
73956b6
Throw errors instead of not doing anything
AbhyudayaSharma Jul 9, 2019
bf50316
Disallow dangerous permissions
AbhyudayaSharma Jul 9, 2019
ce4ada2
Add Support for Configuration-as-Code
AbhyudayaSharma Jul 10, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -4,6 +4,7 @@ work
# Eclipse project files
.settings
.classpath
.factorypath
.project
/nb-configuration.xml

Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -83,8 +83,8 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloudbees-folder</artifactId>
<version>6.1.0</version>
<scope>test</scope>
<version>6.4</version>
AbhyudayaSharma marked this conversation as resolved.
Show resolved Hide resolved
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
Expand Up @@ -839,7 +839,7 @@ private void readRoles(JSONObject formData, final RoleType roleType,
*/
private Role createAdminRole() {
Set<Permission> permissions = new HashSet<>();
for (PermissionGroup group : getGroups(GLOBAL)) {
for (PermissionGroup group : getPermissionGroups(RoleType.Global)) {
for (Permission permission : group) {
permissions.add(permission);
}
Expand All @@ -863,36 +863,16 @@ private String getCurrentUser() {
* @param type Role type
* @return Groups, which should be displayed for a specific role type.
* {@code null} if an unsupported type is defined.
* @deprecated use {@link #getPermissionGroups(RoleType)}
*/
@Nullable
@Deprecated
public List<PermissionGroup> getGroups(@Nonnull String type) {
List<PermissionGroup> groups;
if (type.equals(GLOBAL)) {
groups = new ArrayList<>(PermissionGroup.getAll());
groups.remove(PermissionGroup.get(Permission.class));
}
else if (type.equals(PROJECT)) {
groups = new ArrayList<>(PermissionGroup.getAll());
groups.remove(PermissionGroup.get(Permission.class));
groups.remove(PermissionGroup.get(Hudson.class));
groups.remove(PermissionGroup.get(Computer.class));
groups.remove(PermissionGroup.get(View.class));
}
else if (type.equals(SLAVE)) {
groups = new ArrayList<>(PermissionGroup.getAll());
groups.remove(PermissionGroup.get(Permission.class));
groups.remove(PermissionGroup.get(Hudson.class));
groups.remove(PermissionGroup.get(View.class));

// Project, SCM and Run permissions
groups.remove(PermissionGroup.get(Item.class));
groups.remove(PermissionGroup.get(SCM.class));
groups.remove(PermissionGroup.get(Run.class));
}
else {
groups = null;
try {
return new ArrayList<>(getPermissionGroups(RoleType.fromString(type)));
} catch (IllegalArgumentException e) {
return null;
}
return groups;
}

@Restricted(NoExternalUse.class)
Expand Down Expand Up @@ -941,4 +921,40 @@ else if (type.equals(SLAVE)) {
}
}
}

/**
* Get the needed permissions group valid for the {@link RoleType}
*
* @param type the {@link RoleType}
* @return Groups, which should be displayed for a specific role type.
* @throws IllegalArgumentException when {@code type} is unknown
*/
@Nonnull
public static Set<PermissionGroup> getPermissionGroups(@Nonnull RoleType type) {
Set<PermissionGroup> groups = new HashSet<>(PermissionGroup.getAll());
groups.remove(PermissionGroup.get(Permission.class));

if (type == RoleType.Global) {
return groups;
}

groups.remove(PermissionGroup.get(Hudson.class));
groups.remove(PermissionGroup.get(View.class));

switch (type) {
case Project:
groups.remove(PermissionGroup.get(Computer.class));
break;
case Slave:
// Project, SCM and Run permissions
groups.remove(PermissionGroup.get(Item.class));
groups.remove(PermissionGroup.get(SCM.class));
groups.remove(PermissionGroup.get(Run.class));
break;
default:
throw new IllegalArgumentException("Unknown RoleType");
}

return groups;
}
}
@@ -0,0 +1,202 @@
package io.jenkins.plugins.rolestrategy;

import com.cloudbees.hudson.plugins.folder.Folder;
import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType;
import hudson.Extension;
import hudson.model.ManagementLink;
import hudson.security.ACL;
import hudson.security.ACLContext;
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import io.jenkins.plugins.rolestrategy.misc.FolderRoleCreationRequest;
import io.jenkins.plugins.rolestrategy.misc.GlobalRoleCreationRequest;
import io.jenkins.plugins.rolestrategy.roles.FolderRole;
import io.jenkins.plugins.rolestrategy.roles.GlobalRole;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.rolestrategy.permissions.PermissionHelper;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.json.JsonBody;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy.getPermissionGroups;

@Extension
public class FolderAuthorizationStrategyManagementLink extends ManagementLink {
private static final Logger LOGGER = Logger.getLogger(FolderAuthorizationStrategyManagementLink.class.getName());

@CheckForNull
@Override
public String getIconFileName() {
return "secure.gif";
}

@CheckForNull
@Override
public String getUrlName() {
return "folder-auth";
}

@CheckForNull
@Override
public String getDisplayName() {
return "Folder Authorization Strategy";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to make it localizable for the final implementation

}

@Nonnull
@Restricted(NoExternalUse.class)
public Set<Permission> getGlobalPermissions() {
return getSafePermissions(getPermissionGroups(RoleType.Global));
}

@Nonnull
@Restricted(NoExternalUse.class)
public Set<Permission> getFolderPermissions() {
return getSafePermissions(getPermissionGroups(RoleType.Project));
}

/**
* Adds a {@link GlobalRole} to {@link FolderBasedAuthorizationStrategy}
*
* @param request the request to create the {@link GlobalRole}
*/
@RequirePOST
@Restricted(NoExternalUse.class)
public void doAddGlobalRole(@JsonBody GlobalRoleCreationRequest request) throws IOException {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(Jenkins.ADMINISTER);
AuthorizationStrategy strategy = jenkins.getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
((FolderBasedAuthorizationStrategy) strategy).addGlobalRole(request.getGlobalRole());
}
}

/**
* Assigns {@code sid} to the global role identified by {@code roleName}.
* <p>
* Does not do anything if a role corresponding to the {@code roleName} does not exist.
*
* @param roleName the name of the global to which {@code sid} will be assigned to.
* @param sid the sid of the user/group to be assigned.
* @throws IOException when unable to assign the Sid to the role
*/
@RequirePOST
@Restricted(NoExternalUse.class)
public void doAssignSidToGlobalRole(@QueryParameter(required = true) String roleName,
@QueryParameter(required = true) String sid) throws IOException {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(Jenkins.ADMINISTER);
AuthorizationStrategy strategy = jenkins.getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
((FolderBasedAuthorizationStrategy) strategy).assignSidToGlobalRole(roleName, sid);
}
try {
Stapler.getCurrentResponse().forwardToPreviousPage(Stapler.getCurrentRequest());
} catch (ServletException | IOException e) {
LOGGER.log(Level.WARNING, "Unable to redirect to previous page.");
}
}

/**
* Adds a {@link FolderRole} to {@link FolderBasedAuthorizationStrategy}
*
* @param request the request to create the role
* @throws IOException when unable to add the role
*/
@RequirePOST
@Restricted(NoExternalUse.class)
public void doAddFolderRole(@JsonBody FolderRoleCreationRequest request) throws IOException {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(Jenkins.ADMINISTER);
AuthorizationStrategy strategy = jenkins.getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
((FolderBasedAuthorizationStrategy) strategy).addFolderRole(request.getFolderRole());
}
AbhyudayaSharma marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Assigns {@code sid} to the folder role identified by {@code roleName}.
* <p>
* Does not do anything if a role corresponding to the {@code roleName} does not exist.
*
* @param roleName the name of the global to which {@code sid} will be assigned to.
* @param sid the sid of the user/group to be assigned.
* @throws IOException when unable to assign the Sid to the role
*/
@RequirePOST
@Restricted(NoExternalUse.class)
public void doAssignSidToFolderRole(@QueryParameter(required = true) String roleName,
@QueryParameter(required = true) String sid) throws IOException {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(Jenkins.ADMINISTER);
AuthorizationStrategy strategy = jenkins.getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
((FolderBasedAuthorizationStrategy) strategy).assignSidToFolderRole(roleName, sid);
}
try {
Stapler.getCurrentResponse().forwardToPreviousPage(Stapler.getCurrentRequest());
} catch (ServletException | IOException e) {
LOGGER.log(Level.WARNING, "Unable to redirect to previous page.");
}
}

@Nonnull
@Restricted(NoExternalUse.class)
public Set<GlobalRole> getGlobalRoles() {
AuthorizationStrategy strategy = Jenkins.getInstance().getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
return ((FolderBasedAuthorizationStrategy) strategy).getGlobalRoles();
}
return Collections.emptySet();
}

/**
* Get all {@link Folder}s in the system
*
* @return folders in the system
*/
@Nonnull
@Restricted(NoExternalUse.class)
public List<Folder> getAllFolders() {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(Jenkins.ADMINISTER);
List<Folder> folders;

try (ACLContext ignored = ACL.as(ACL.SYSTEM)) {
folders = jenkins.getAllItems(Folder.class);
AbhyudayaSharma marked this conversation as resolved.
Show resolved Hide resolved
}

return folders;
}

@Nonnull
@Restricted(NoExternalUse.class)
public Set<FolderRole> getFolderRoles() {
AuthorizationStrategy strategy = Jenkins.getInstance().getAuthorizationStrategy();
if (strategy instanceof FolderBasedAuthorizationStrategy) {
return ((FolderBasedAuthorizationStrategy) strategy).getFolderRoles();
}
return Collections.emptySet();
}

private static Set<Permission> getSafePermissions(Set<PermissionGroup> groups) {
HashSet<Permission> safePermissions = new HashSet<>();
groups.stream().map(PermissionGroup::getPermissions).forEach(safePermissions::addAll);
safePermissions.removeAll(PermissionHelper.DANGEROUS_PERMISSIONS);
return safePermissions;
}
}