From a5f27678f6d61a330597b9b157ca7468158835f5 Mon Sep 17 00:00:00 2001 From: Markus Winter Date: Fri, 4 Aug 2023 14:56:37 +0200 Subject: [PATCH] [JENKINS-51672][JENKINS-41731] get users role in pipeline (#324) added the pipeline steps currentUserGlobalRoles currentUserItemRoles --- README.md | 15 +++ pom.xml | 14 +++ .../hudson/plugins/rolestrategy/RoleMap.java | 55 ++++++++- .../pipeline/AbstractUserRolesStep.java | 83 +++++++++++++ .../pipeline/UserGlobalRoles.java | 49 ++++++++ .../rolestrategy/pipeline/UserItemRoles.java | 98 +++++++++++++++ .../pipeline/UserGlobalRoles/help.html | 7 ++ .../pipeline/UserItemRoles/config.jelly | 6 + .../UserItemRoles/help-showAllRoles.html | 3 + .../pipeline/UserItemRoles/help.html | 7 ++ .../pipeline/UserGlobalRolesTest.java | 115 ++++++++++++++++++ .../pipeline/UserItemRolesTest.java | 103 ++++++++++++++++ .../Configuration-as-Code-pipeline.yml | 67 ++++++++++ 13 files changed, 618 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/AbstractUserRolesStep.java create mode 100644 src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles.java create mode 100644 src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles.java create mode 100644 src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles/help.html create mode 100644 src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/config.jelly create mode 100644 src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help-showAllRoles.html create mode 100644 src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help.html create mode 100644 src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRolesTest.java create mode 100644 src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRolesTest.java create mode 100644 src/test/resources/org/jenkinsci/plugins/rolestrategy/pipeline/Configuration-as-Code-pipeline.yml diff --git a/README.md b/README.md index 9491b13f..9fe1c548 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,21 @@ You can assign roles to users and user groups using the _Assign Roles_ screen ![Assign roles](/docs/images/assignRoles.png) +### Getting roles in pipelines +There are 2 steps available in pipeline jobs that allow to get the roles of the user running the build. +When the build was triggered by a user via the UI or the REST API, the roles of this user are returned. In case the build was triggered +by the times or an SCM event there is no dedicated user available and the `SYSTEM` user is used. This user is considered like an admin and will have all roles.
+With the [Authorize Project](https://plugins.jenkins.io/authorize-project/) plugin, it is possible to make builds triggered by timer or an SCM event +to run as a specific user which is then used or run as `anonymous`. For `anonymous` it means no roles are returned. The user that triggered the build will always take +precedence over the user that is configured via `Authorize Project`. + +#### currentUserGlobalRoles +The step `currentUserGlobalRoles` will return all global roles of the user. + +#### currentUserItemRoles +The step `currentUserGlobalRoles` will return the item roles of the user. By default, it returns only those roles that +match the currently building pipeline. The parameter `showAllRoles` will return all item roles of the user. + ### Rest API The Rest API allows to query the current roles and assignments and to do changes to them. diff --git a/pom.xml b/pom.xml index c95105ed..9fa5cb22 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,11 @@ cloudbees-folder true + + org.jenkins-ci.plugins.workflow + workflow-step-api + true + io.jenkins.configuration-as-code test-harness @@ -106,6 +111,15 @@ org.jenkins-ci.plugins authorize-project 1.7.0 + + + org.jenkins-ci.plugins.workflow + workflow-job + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps test diff --git a/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleMap.java b/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleMap.java index 8c667f0e..ee41c965 100644 --- a/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleMap.java +++ b/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleMap.java @@ -70,8 +70,10 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.DataBoundConstructor; +import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * Class holding a map for each kind of {@link AccessControlled} object, associating each {@link Role} with the @@ -402,8 +404,8 @@ public void deleteSids(String sid) { /** * Clear specific role associated to the given sid. * - * @param sid The sid for wich you want to clear the {@link Role}s - * @param rolename The role for wich you want to clear the {@link Role}s + * @param sid The sid for which you want to clear the {@link Role}s + * @param rolename The role for which you want to clear the {@link Role}s * @since 2.6.0 */ public void deleteRoleSid(PermissionEntry sid, String rolename) { @@ -421,8 +423,8 @@ public void deleteRoleSid(PermissionEntry sid, String rolename) { * Clear specific role associated to the given sid. * This will only find sids of type {@link AuthorizationType#EITHER} * - * @param sid The sid for wich you want to clear the {@link Role}s - * @param rolename The role for wich you want to clear the {@link Role}s + * @param sid The sid for which you want to clear the {@link Role}s + * @param rolename The role for which you want to clear the {@link Role}s * @since 2.6.0 * @deprecated use {@link #deleteRoleSid(PermissionEntry, String)} */ @@ -597,6 +599,51 @@ public Set getSidsForRole(String roleName) { return null; } + /** + * Get all roles associated with the given User. + * + * @param user The User for which to get the roles + * @return a set of roles + * @throws UsernameNotFoundException when user is not found + */ + @NonNull + public Set getRolesForUser(User user) throws UsernameNotFoundException { + return getRolesForAuth(user.impersonate2()); + } + + /** + * Get all roles associated with the given Authentication. + * + * @param auth The Authentication for which to get the roles + * @return a set of roles + */ + @NonNull + @Restricted(NoExternalUse.class) + public Set getRolesForAuth(Authentication auth) { + PermissionEntry userEntry = new PermissionEntry(AuthorizationType.USER, auth.getPrincipal().toString()); + Set roleSet = new HashSet<>(getRolesForSidEntry(userEntry)); + for (GrantedAuthority group : auth.getAuthorities()) { + PermissionEntry groupEntry = new PermissionEntry(AuthorizationType.GROUP, group.getAuthority()); + roleSet.addAll(getRolesForSidEntry(groupEntry)); + } + return roleSet; + } + + private Set getRolesForSidEntry(PermissionEntry entry) { + + Set roleSet = new HashSet<>(); + new RoleWalker() { + @Override + public void perform(Role current) { + if (grantedRoles.get(current).contains(entry)) { + roleSet.add(current.getName()); + } + } + }; + + return roleSet; + } + /** * Create a sub-map of this {@link RoleMap} containing {@link Role}s that are applicable on the given * {@code itemNamePrefix}. diff --git a/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/AbstractUserRolesStep.java b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/AbstractUserRolesStep.java new file mode 100644 index 00000000..6ace6f7b --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/AbstractUserRolesStep.java @@ -0,0 +1,83 @@ +package org.jenkinsci.plugins.rolestrategy.pipeline; + +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 com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.Cause; +import hudson.model.Run; +import hudson.model.User; +import hudson.security.ACL; +import hudson.security.AuthorizationStrategy; +import java.io.IOException; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import jenkins.model.Jenkins; +import org.jenkinsci.plugins.workflow.steps.Step; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; +import org.springframework.security.core.Authentication; + +/** + * Base class for the pipeline steps. + */ +public abstract class AbstractUserRolesStep extends Step { + + /** + * Step Execution. + */ + protected static class Execution extends SynchronousNonBlockingStepExecution> { + protected final RoleType roleType; + + public Execution(@NonNull StepContext context, RoleType roleType) { + super(context); + this.roleType = roleType; + } + + protected RoleMap getRoleMap() throws IOException, InterruptedException { + AuthorizationStrategy strategy = Jenkins.get().getAuthorizationStrategy(); + if (strategy instanceof RoleBasedAuthorizationStrategy) { + RoleBasedAuthorizationStrategy rbas = (RoleBasedAuthorizationStrategy) strategy; + return rbas.getRoleMap(roleType); + } + return null; + } + + @Override + protected Set run() throws Exception { + Set roleSet = new HashSet<>(); + Authentication auth = getAuthentication(); + if (auth == null) { + return roleSet; + } + RoleMap roleMap = getRoleMap(); + if (roleMap != null) { + if (auth == ACL.SYSTEM2) { + return roleMap.getRoles().stream().map(Role::getName).collect(Collectors.toSet()); + } + return roleMap.getRolesForAuth(auth); + } + return roleSet; + } + + + private Authentication getAuthentication() throws IOException, InterruptedException { + final Run run = Objects.requireNonNull(getContext().get(Run.class)); + Cause.UserIdCause cause = run.getCause(Cause.UserIdCause.class); + if (cause != null) { + User causeUser = User.getById(cause.getUserId(), false); + if (causeUser != null) { + return causeUser.impersonate2(); + } + } + Authentication auth = Jenkins.getAuthentication2(); + if (ACL.isAnonymous2(auth)) { + return null; + } + return auth; + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles.java b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles.java new file mode 100644 index 00000000..1dad3cb0 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles.java @@ -0,0 +1,49 @@ +package org.jenkinsci.plugins.rolestrategy.pipeline; + +import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Extension; +import hudson.model.Run; +import java.util.Collections; +import java.util.Set; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.jenkinsci.plugins.workflow.steps.StepDescriptor; +import org.jenkinsci.plugins.workflow.steps.StepExecution; +import org.kohsuke.stapler.DataBoundConstructor; + +/** + * Pipeline step that returns the users global roles. + */ +public class UserGlobalRoles extends AbstractUserRolesStep { + + @DataBoundConstructor + public UserGlobalRoles() { + } + + @Override + public StepExecution start(StepContext context) throws Exception { + return new Execution(context, RoleType.Global); + } + + /** + * The descriptor of the step. + */ + @Extension + public static final class DescriptorImpl extends StepDescriptor { + + @Override + public Set> getRequiredContext() { + return Collections.singleton(Run.class); + } + + @NonNull + @Override public String getDisplayName() { + return "Current Users Global Roles"; + } + + @Override + public String getFunctionName() { + return "currentUserGlobalRoles"; + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles.java b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles.java new file mode 100644 index 00000000..3a466f36 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles.java @@ -0,0 +1,98 @@ +package org.jenkinsci.plugins.rolestrategy.pipeline; + +import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy; +import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap; +import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Extension; +import hudson.model.Job; +import hudson.model.Run; +import hudson.security.AuthorizationStrategy; +import java.io.IOException; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import jenkins.model.Jenkins; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.jenkinsci.plugins.workflow.steps.StepDescriptor; +import org.jenkinsci.plugins.workflow.steps.StepExecution; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; + +/** + * Pipeline step that returns the users item roles. + */ +public class UserItemRoles extends AbstractUserRolesStep { + + private boolean showAllRoles; + + @DataBoundConstructor + public UserItemRoles() { + } + + public boolean isShowAllRoles() { + return showAllRoles; + } + + @DataBoundSetter + public void setShowAllRoles(boolean showAllRoles) { + this.showAllRoles = showAllRoles; + } + + @Override + public StepExecution start(StepContext context) throws Exception { + return new ItemRolesExecution(context, RoleType.Project, showAllRoles); + } + + /** + * Step Execution. + */ + public static class ItemRolesExecution extends Execution { + + private final boolean showAllRoles; + + public ItemRolesExecution(@NonNull StepContext context, RoleType roleType, boolean showAllRoles) { + super(context, roleType); + this.showAllRoles = showAllRoles; + } + + @Override + protected RoleMap getRoleMap() throws IOException, InterruptedException { + AuthorizationStrategy strategy = Jenkins.get().getAuthorizationStrategy(); + if (strategy instanceof RoleBasedAuthorizationStrategy) { + RoleBasedAuthorizationStrategy rbas = (RoleBasedAuthorizationStrategy) strategy; + RoleMap roleMap = rbas.getRoleMap(roleType); + if (showAllRoles) { + return roleMap; + } else { + final Run run = Objects.requireNonNull(getContext().get(Run.class)); + Job job = run.getParent(); + return roleMap.newMatchingRoleMap(job.getFullName()); + } + } + return null; + } + } + + /** + * The descriptor. + */ + @Extension + public static final class DescriptorImpl extends StepDescriptor { + + @Override + public Set> getRequiredContext() { + return Collections.singleton(Run.class); + } + + @NonNull + @Override public String getDisplayName() { + return "Current Users Item Roles"; + } + + @Override + public String getFunctionName() { + return "currentUserItemRoles"; + } + } +} diff --git a/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles/help.html b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles/help.html new file mode 100644 index 00000000..b53d5114 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRoles/help.html @@ -0,0 +1,7 @@ +
+ Returns a list of all global roles of the user that started the build. This includes roles assigned via groups. + When the run is triggered by an SCM event or by the timer, the build usually runs as the System user. This user is + considered as having all roles.
+ You can use the Authorize Project plugin + to run the builds as a different user. When running as anonymous, an empty list is returned. +
diff --git a/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/config.jelly b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/config.jelly new file mode 100644 index 00000000..7d788e67 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/config.jelly @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help-showAllRoles.html b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help-showAllRoles.html new file mode 100644 index 00000000..223d41ff --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help-showAllRoles.html @@ -0,0 +1,3 @@ +
+ If checked, all item roles of the user are returned. Otherwise only roles matching the pipeline job are returned. +
diff --git a/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help.html b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help.html new file mode 100644 index 00000000..342eba77 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRoles/help.html @@ -0,0 +1,7 @@ +
+ Returns a list of all item roles of the user that started the build. This includes roles assigned via groups. + When the run is triggered by an SCM event or by the timer, the build usually runs as the System user. This user is + considered as having all roles.
+ You can use the Authorize Project plugin + to run the builds as a different user. When running as anonymous, an empty list is returned. +
diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRolesTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRolesTest.java new file mode 100644 index 00000000..dc9f43a2 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserGlobalRolesTest.java @@ -0,0 +1,115 @@ +package org.jenkinsci.plugins.rolestrategy.pipeline; + +import hudson.model.Cause; +import hudson.model.User; +import hudson.security.ACL; +import hudson.security.ACLContext; +import hudson.triggers.TimerTrigger; +import io.jenkins.plugins.casc.misc.ConfiguredWithCode; +import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; +import java.io.IOException; +import jenkins.security.QueueItemAuthenticatorConfiguration; +import org.jenkinsci.plugins.authorizeproject.GlobalQueueItemAuthenticator; +import org.jenkinsci.plugins.authorizeproject.strategy.AnonymousAuthorizationStrategy; +import org.jenkinsci.plugins.authorizeproject.strategy.SpecificUsersAuthorizationStrategy; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule.DummySecurityRealm; + +public class UserGlobalRolesTest { + @Rule + public JenkinsConfiguredWithCodeRule jenkinsRule = new JenkinsConfiguredWithCodeRule(); + + private WorkflowJob pipeline; + + @Before + public void setup() throws IOException { + DummySecurityRealm securityRealm = jenkinsRule.createDummySecurityRealm(); + jenkinsRule.jenkins.setSecurityRealm(securityRealm); + securityRealm.addGroups("builder1", "readers"); + securityRealm.addGroups("builder2", "readers"); + User.getById("builder1", true); + User.getById("builder2", true); + pipeline = jenkinsRule.createProject(WorkflowJob.class, "pipeline"); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void systemUserHasAllGlobalRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserGlobalRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Global Role: \" + r)\n" + + "}", true)); + WorkflowRun run = jenkinsRule.buildAndAssertSuccess(pipeline); + jenkinsRule.assertLogContains("adminRole", run); + jenkinsRule.assertLogContains("readonlyRole", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void builderUserHasGlobalRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserGlobalRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Global Role: \" + r)\n" + + "}", true)); + try (ACLContext c = ACL.as(User.getById("builder1", true))) { + pipeline.scheduleBuild(0, new Cause.UserIdCause()); + } + jenkinsRule.waitUntilNoActivity(); + WorkflowRun run = pipeline.getLastBuild(); + jenkinsRule.assertLogContains("readonlyRole", run); + jenkinsRule.assertLogNotContains("adminRole", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void anonymousUserHasNoRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserGlobalRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Global Role: \" + r)\n" + + "}\n" + + "roles = currentUserItemRoles showAllRoles: true\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + QueueItemAuthenticatorConfiguration.get().getAuthenticators() + .add(new GlobalQueueItemAuthenticator(new AnonymousAuthorizationStrategy())); + pipeline.scheduleBuild(0, new Cause.UserIdCause()); + jenkinsRule.waitUntilNoActivity(); + WorkflowRun run = pipeline.getLastBuild(); + jenkinsRule.assertLogNotContains("readonlyRole", run); + jenkinsRule.assertLogNotContains("adminRole", run); + jenkinsRule.assertLogNotContains("builder1Role", run); + jenkinsRule.assertLogNotContains("builder2Role", run); + jenkinsRule.assertLogNotContains("reader1Role", run); + jenkinsRule.assertLogNotContains("reader2Role", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void builderUserHasRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserGlobalRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Global Role: \" + r)\n" + + "}\n" + + "roles = currentUserItemRoles showAllRoles: true\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + QueueItemAuthenticatorConfiguration.get().getAuthenticators() + .add(new GlobalQueueItemAuthenticator(new SpecificUsersAuthorizationStrategy("builder1"))); + pipeline.scheduleBuild(0, new TimerTrigger.TimerTriggerCause()); + jenkinsRule.waitUntilNoActivity(); + WorkflowRun run = pipeline.getLastBuild(); + jenkinsRule.assertLogContains("readonlyRole", run); + jenkinsRule.assertLogNotContains("adminRole", run); + jenkinsRule.assertLogContains("builder1Role", run); + jenkinsRule.assertLogNotContains("builder2Role", run); + jenkinsRule.assertLogContains("reader1Role", run); + jenkinsRule.assertLogContains("reader2Role", run); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRolesTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRolesTest.java new file mode 100644 index 00000000..98639b12 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/pipeline/UserItemRolesTest.java @@ -0,0 +1,103 @@ +package org.jenkinsci.plugins.rolestrategy.pipeline; + +import hudson.model.Cause; +import hudson.model.User; +import hudson.security.ACL; +import hudson.security.ACLContext; +import hudson.triggers.TimerTrigger; +import io.jenkins.plugins.casc.misc.ConfiguredWithCode; +import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; +import java.io.IOException; +import jenkins.security.QueueItemAuthenticatorConfiguration; +import org.jenkinsci.plugins.authorizeproject.GlobalQueueItemAuthenticator; +import org.jenkinsci.plugins.authorizeproject.strategy.AnonymousAuthorizationStrategy; +import org.jenkinsci.plugins.authorizeproject.strategy.SpecificUsersAuthorizationStrategy; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule.DummySecurityRealm; + +public class UserItemRolesTest { + @Rule + public JenkinsConfiguredWithCodeRule jenkinsRule = new JenkinsConfiguredWithCodeRule(); + + private WorkflowJob pipeline; + + @Before + public void setup() throws IOException { + DummySecurityRealm securityRealm = jenkinsRule.createDummySecurityRealm(); + jenkinsRule.jenkins.setSecurityRealm(securityRealm); + securityRealm.addGroups("builder1", "readers"); + securityRealm.addGroups("builder2", "readers"); + User.getById("builder1", true); + User.getById("builder2", true); + pipeline = jenkinsRule.createProject(WorkflowJob.class, "pipeline"); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void systemUserHasAllItemRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserItemRoles showAllRoles: true\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + WorkflowRun run = jenkinsRule.buildAndAssertSuccess(pipeline); + jenkinsRule.assertLogContains("builder1Role", run); + jenkinsRule.assertLogContains("builder2Role", run); + jenkinsRule.assertLogContains("reader1Role", run); + jenkinsRule.assertLogContains("reader2Role", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void systemUserHasAllMatchingItemRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserItemRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + WorkflowRun run = jenkinsRule.buildAndAssertSuccess(pipeline); + jenkinsRule.assertLogContains("builder1Role", run); + jenkinsRule.assertLogNotContains("builder2Role", run); + jenkinsRule.assertLogContains("reader1Role", run); + jenkinsRule.assertLogNotContains("reader2Role", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void builderUserHasItemRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserItemRoles showAllRoles: true\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + try (ACLContext c = ACL.as(User.getById("builder1", true))) { + pipeline.scheduleBuild(0, new Cause.UserIdCause()); + } + jenkinsRule.waitUntilNoActivity(); + WorkflowRun run = pipeline.getLastBuild(); + jenkinsRule.assertLogContains("builder1Role", run); + jenkinsRule.assertLogNotContains("builder2Role", run); + jenkinsRule.assertLogContains("reader1Role", run); + jenkinsRule.assertLogContains("reader2Role", run); + } + + @Test + @ConfiguredWithCode("Configuration-as-Code-pipeline.yml") + public void builderUserHasMatchingItemRoles() throws Exception { + pipeline.setDefinition(new CpsFlowDefinition("roles = currentUserItemRoles()\n" + + "for (r in roles) {\n" + + " echo(\"Item Role: \" + r)\n" + + "}", true)); + try (ACLContext c = ACL.as(User.getById("builder1", true))) { + pipeline.scheduleBuild(0, new Cause.UserIdCause()); + } + jenkinsRule.waitUntilNoActivity(); + WorkflowRun run = pipeline.getLastBuild(); + jenkinsRule.assertLogContains("builder1Role", run); + jenkinsRule.assertLogNotContains("builder2Role", run); + jenkinsRule.assertLogContains("reader1Role", run); + jenkinsRule.assertLogNotContains("reader2Role", run); + } +} diff --git a/src/test/resources/org/jenkinsci/plugins/rolestrategy/pipeline/Configuration-as-Code-pipeline.yml b/src/test/resources/org/jenkinsci/plugins/rolestrategy/pipeline/Configuration-as-Code-pipeline.yml new file mode 100644 index 00000000..32e0dd35 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/rolestrategy/pipeline/Configuration-as-Code-pipeline.yml @@ -0,0 +1,67 @@ +jenkins: + authorizationStrategy: + roleBased: + roles: + global: + - name: "adminRole" + description: "Jenkins administrators" + permissions: + - "Overall/Administer" + entries: + - user: "admin" + - name: "readonlyRole" + description: "Read-only users" + permissions: + - "Overall/Read" + entries: + - group: "readers" + items: + - name: "builder1Role" + description: "build job pipeline" + pattern: "^pipeline$" + permissions: + - "Job/Configure" + - "Job/Workspace" + - "Job/Read" + - "Job/Build" + - "Job/Delete" + - "Job/Cancel" + entries: + - user: "builder1" + - name: "reader1Role" + description: "read job pipeline" + pattern: "^pipeline$" + permissions: + - "Job/Read" + entries: + - group: "readers" + - name: "builder2Role" + description: "build job pipeline 2" + pattern: "^job-pipeline$" + permissions: + - "Job/Configure" + - "Job/Read" + - "Job/Build" + - "Job/Delete" + entries: + - user: "builder2" + - name: "reader2Role" + description: "read job pipeline 2" + pattern: "^job-pipeline$" + permissions: + - "Job/Read" + entries: + - group: "readers" + + # System for test + securityRealm: + local: + allowsSignup: false + users: + - id: "admin" + password: "1234" + - id: "builder1" + password: "builder1" + - id: "builder2" + password: "builder2" +