Merge pull request #94 from jenkinsci/16-global-matrix-auth

[WiP] First draft for Global Matrix Authorization
This commit is contained in:
Ewelina Wilkosz 2018-02-12 12:14:06 +01:00 committed by GitHub
commit a047aeaf5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 269 additions and 9 deletions

View File

@ -0,0 +1,12 @@
jenkins:
authorizationStrategy:
globalMatrix:
grantedPermissions:
- group:
name: "anonymous"
permissions:
- "hudson.model.Hudson.Read"
- group
name: "authenticated"
permissions:
- "hudson.model.Hudson.Administer"

View File

@ -92,6 +92,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>matrix-auth</artifactId>
<version>2.2</version>
<optional>true</optional>
</dependency>
<!-- tested plugins -->

View File

@ -42,6 +42,5 @@ public class HudsonPrivateSecurityRealmConfigurator extends DataBoundConfigurato
this.password = password;
}
}
}

View File

@ -0,0 +1,59 @@
package org.jenkinsci.plugins.casc.integrations.globalmatrixauth;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.security.GlobalMatrixAuthorizationStrategy;
import hudson.security.Permission;
import org.jenkinsci.plugins.casc.Attribute;
import org.jenkinsci.plugins.casc.Configurator;
import org.jenkinsci.plugins.casc.RootElementConfigurator;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import java.lang.reflect.Field;
import java.util.*;
/**
* @author Mads Nielsen
* @since TODO
*/
@Extension(optional = true)
@Restricted(NoExternalUse.class)
public class GlobalMatrixAuthorizationStrategyConfigurator extends Configurator<GlobalMatrixAuthorizationStrategy> implements RootElementConfigurator {
@Override
public String getName() {
return "globalMatrix";
}
@Override
public Class<GlobalMatrixAuthorizationStrategy> getTarget() {
return GlobalMatrixAuthorizationStrategy.class;
}
@Override
@SuppressFBWarnings(value = "DM_NEW_FOR_GETCLASS", justification = "We need a fully qualified type to do proper attribute binding")
public Set<Attribute> describe() {
return Collections.singleton(new Attribute<GroupPermissionDefinition>("grantedPermissions", new HashSet<GroupPermissionDefinition>().getClass()));
}
@Override
public GlobalMatrixAuthorizationStrategy configure(Object config) throws Exception {
Map map = (Map) config;
Collection o = (Collection<?>)map.get("grantedPermissions");
Configurator<GroupPermissionDefinition> permissionConfigurator = Configurator.lookup(GroupPermissionDefinition.class);
Map<Permission,Set<String>> grantedPermissions = new HashMap<>();
for(Object entry : o) {
GroupPermissionDefinition gpd = permissionConfigurator.configure(entry);
//We transform the linear list to a matrix (Where permission is the key instead)
gpd.grantPermission(grantedPermissions);
}
//TODO: Once change is in place for GlobalMatrixAuthentication. Switch away from reflection
GlobalMatrixAuthorizationStrategy gms = new GlobalMatrixAuthorizationStrategy();
Field f = gms.getClass().getDeclaredField("grantedPermissions");
f.setAccessible(true);
f.set(gms, grantedPermissions);
return gms;
}
}

View File

@ -0,0 +1,60 @@
package org.jenkinsci.plugins.casc.integrations.globalmatrixauth;
import hudson.security.Permission;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.*;
import java.util.logging.Logger;
/**
* @author Mads Nielsen
* @since TODO
*/
@Restricted(NoExternalUse.class)
public class GroupPermissionDefinition {
private String name;
private Collection<String> permissions;
private static final Logger LOGGER = Logger.getLogger(GroupPermissionDefinition.class.getName());
@DataBoundConstructor
public GroupPermissionDefinition(String name, Collection<String> permissions) {
this.name = name;
this.permissions = permissions != null ? Collections.unmodifiableCollection(permissions) : Collections.EMPTY_SET;
}
public void grantPermission(Map<Permission,Set<String>> grantedPermissions) {
for(String permission : permissions) {
//Permission pm = Permission.fromId(permission);
Permission pm = PermissionFinder.findPermission(permission);
if(pm != null) {
if (grantedPermissions.containsKey(pm)) {
grantedPermissions.get(pm).add(name);
} else {
HashSet<String> s = new HashSet<>();
s.add(name);
grantedPermissions.put(pm, s);
}
} else {
LOGGER.warning(String.format("Ignoring unknown permission with id: '%s'", permission));
}
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return String.format("'%s' granted [%s]", name, permissions != null ? String.join(",", permissions) : "" );
}
}

View File

@ -0,0 +1,48 @@
package org.jenkinsci.plugins.casc.integrations.globalmatrixauth;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import jenkins.model.Jenkins;
import javax.annotation.CheckForNull;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by mads on 2/9/18.
*/
public class PermissionFinder {
/** For Matrix Auth - Title/Permission **/
private static final Pattern PERMISSION_PATTERN = Pattern.compile("(\\w+)/(\\w+)");
/**
* Attempt to match a given permission to what is defined in the UI.
* TODO: Refector this away when proper permission API is in place for C-as-C
* @param id String of the form "Title/Permission" (Look in the UI) for a particular permission
* @return a matched permission
*/
@CheckForNull
public static Permission findPermission(String id) {
List<PermissionGroup> pgs = PermissionGroup.getAll();
Matcher m = PERMISSION_PATTERN.matcher(id);
if(m.matches()) {
String owner = m.group(1);
String name = m.group(2);
for(PermissionGroup pg : pgs) {
if(pg.owner.equals(Permission.class)) {
continue;
}
//How do we do this properly, we want to mimic the UI as best as possible. So the logic conclusion is
//That when you want admin to be Overall/Administer you put that in. Overall being the group title...
//Name being the Permssion you want to set in the matrix.
if(pg.title.toString().equals(owner)) {
return Permission.fromId(pg.owner.getName()+"."+name);
}
}
}
return null;
}
}

View File

@ -1,7 +1,6 @@
package org.jenkinsci.plugins.casc.integrations;
package org.jenkinsci.plugins.casc.integrations.rolebasedauth;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.michelin.cio.hudson.plugins.rolestrategy.Role;
import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy;
import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap;
@ -21,7 +20,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**

View File

@ -1,4 +1,4 @@
package org.jenkinsci.plugins.casc.integrations;
package org.jenkinsci.plugins.casc.integrations.rolebasedauth;
import com.michelin.cio.hudson.plugins.rolestrategy.Role;
import org.kohsuke.accmod.Restricted;

View File

@ -0,0 +1,54 @@
package org.jenkinsci.plugins.casc.integrations.globalmatrixauth;
import hudson.security.AuthorizationStrategy;
import hudson.security.GlobalMatrixAuthorizationStrategy;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.casc.ConfigurationAsCode;
import org.jenkinsci.plugins.casc.Configurator;
import org.junit.ClassRule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Mads Nielsen
* @since TODO
*/
public class GlobalMatrixAuthorizationTest {
@ClassRule
public static JenkinsRule j = new JenkinsRule();
@Test
public void shouldReturnCustomConfigurator() {
Configurator configurator = Configurator.lookup(GlobalMatrixAuthorizationStrategy.class);
assertNotNull("Failed to find configurator for GlobalMatrixAuthorizationStrategy", configurator);
assertEquals("Retrieved wrong configurator", GlobalMatrixAuthorizationStrategyConfigurator.class, configurator.getClass());
}
@Test
public void shouldReturnCustomConfiguratorForBaseType() {
Configurator c = Configurator.lookupForBaseType(AuthorizationStrategy.class, "globalMatrix");
assertNotNull("Failed to find configurator for GlobalMatrixAuthorizationStrategy", c);
assertEquals("Retrieved wrong configurator", GlobalMatrixAuthorizationStrategyConfigurator.class, c.getClass());
Configurator.lookup(GlobalMatrixAuthorizationStrategy.class);
}
@Test
public void checkCorrectlyConfiguredPermissions() throws Exception {
ConfigurationAsCode.configure(getClass().getResourceAsStream("GlobalMatrixStrategy.yml"));
assertEquals("The configured instance must use the Global Matrix Authentication Strategy", GlobalMatrixAuthorizationStrategy.class, Jenkins.getInstance().getAuthorizationStrategy().getClass());
GlobalMatrixAuthorizationStrategy gms = (GlobalMatrixAuthorizationStrategy)Jenkins.getInstance().getAuthorizationStrategy();
List<String> adminPermission = new ArrayList<>(gms.getGrantedPermissions().get(Jenkins.ADMINISTER));
assertEquals("authenticated", adminPermission.get(0));
List<String> readPermission = new ArrayList<>(gms.getGrantedPermissions().get(Jenkins.READ));
assertEquals("anonymous", readPermission.get(0));
}
}

View File

@ -1,15 +1,15 @@
package org.jenkinsci.plugins.casc.integrations;
package org.jenkinsci.plugins.casc.integrations.rolebasedauth;
import com.michelin.cio.hudson.plugins.rolestrategy.Role;
import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy;
import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap;
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import jenkins.model.Jenkins;
import static org.hamcrest.CoreMatchers.*;
import org.jenkinsci.plugins.casc.ConfigurationAsCode;
import org.jenkinsci.plugins.casc.Configurator;
import org.jenkinsci.plugins.casc.integrations.rolebasedauth.RoleBasedAuthorizationStrategyConfigurator;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
@ -50,9 +50,10 @@ public class RoleStrategyTest {
@Test
@Issue("Issue #48")
public void shouldReadRolesCorrectly() throws Exception {
ConfigurationAsCode.configure(getClass().getResourceAsStream("role-strategy/RoleStrategy1.yml"));
ConfigurationAsCode.configure(getClass().getResourceAsStream("RoleStrategy1.yml"));
final Jenkins jenkins = Jenkins.getInstance();
AuthorizationStrategy s = jenkins.getAuthorizationStrategy();
assertThat("Authorization Strategy has been read incorrectly",
s, instanceOf(RoleBasedAuthorizationStrategy.class));

View File

@ -0,0 +1,23 @@
jenkins:
systemMessage: "Welcome!"
numExecutors: 4
scmCheckoutRetryCount: 2
mode: NORMAL
scmCheckoutRetryCount: 4
securityRealm:
local:
allowsSignup: false
users:
- id: test
password: test
authorizationStrategy:
globalMatrix:
grantedPermissions:
- name: "anonymous"
permissions:
- "Overall/Read"
- name: "authenticated"
permissions:
- "Overall/Administer"