Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #5 from jenkinsci/JENKINS-25726
[JENKINS-25726, JENKINS-25771] - StartedByUser and StartedByMemberOfGroup restrictions
  • Loading branch information
oleg-nenashev committed Jan 16, 2015
2 parents 47b292f + 03b9d59 commit 6767337
Show file tree
Hide file tree
Showing 16 changed files with 767 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.509.2</version>
<version>1.509.3</version>
</parent>

<groupId>com.synopsys.arc.jenkinsci.plugins</groupId>
Expand Down
@@ -0,0 +1,131 @@
/*
* The MIT License
*
* Copyright 2015 Oleg Nenashev <o.v.nenashev@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.job;

import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.JobRestriction;
import hudson.model.Action;
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.Queue;
import hudson.model.Run;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
* Abstract class, which defines the logic of UserCause-based restrictions.
* @author Christopher Suarez
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
* @since 0.4
* @see StartedByUserRestriction
* @see StartedByMemberOfGroupRestriction
*/
public abstract class AbstractUserCauseRestriction extends JobRestriction {

/**
* Enables the check of upstream projects
*/
private final boolean checkUpstreamProjects;

public AbstractUserCauseRestriction(boolean checkUpstreamProjects) {
this.checkUpstreamProjects = checkUpstreamProjects;
}

public final boolean isCheckUpstreamProjects() {
return checkUpstreamProjects;
}

/**
* Check if the method accepts the specified user.
* The user will be automatically extracted by the logic in {@link AbstractUserCauseRestriction}.
* @param userId User id
* @return true if the restriction accepts the user
*/
abstract protected boolean acceptsUser(@CheckForNull String userId);

/* package */ boolean canTake(@Nonnull List<Cause> causes) {
boolean userIdCause = false;
boolean rebuildCause = false;
boolean upstreamCause = false;

boolean aUserIdWasNotAccepted = false;
boolean userIdCauseExists = false;

for (@CheckForNull Cause cause : causes) {
if (cause == null) {
continue; // Protection from the bug in old core versions
}

// Check user causes
if (cause.getClass().equals(Cause.UserIdCause.class) && !aUserIdWasNotAccepted) {
userIdCauseExists = true;
//if several userIdCauses exists, be defensive and don't allow if one is not accepted.
final @CheckForNull String startedBy = ((Cause.UserIdCause) cause).getUserId();
if (acceptsUser(startedBy)) {
userIdCause = true;
} else {
aUserIdWasNotAccepted = true;
userIdCause = false;
}
}

// Check upstream projects if required
if (checkUpstreamProjects && cause.getClass().equals(Cause.UpstreamCause.class)) {
final List<Cause> upstreamCauses = ((Cause.UpstreamCause) cause).getUpstreamCauses();
// Recursive call to iterate through all above
if (canTake(upstreamCauses)) {
upstreamCause = true;
}
}

// TODO: Check rebuild causes
}

//userId has preceedence
if (userIdCauseExists) {
return userIdCause;
} else { //If no update cause exists we should also return false...
return upstreamCause;
}
}

@Override
public boolean canTake(Queue.BuildableItem item) {
final List<Cause> causes = new ArrayList<Cause>();
for (Action action : item.getActions()) {
if (action instanceof CauseAction) {
CauseAction causeAction = (CauseAction) action;
causes.addAll(causeAction.getCauses());
}
}
return canTake(causes);
}

@Override
public boolean canTake(Run run) {
return canTake(run.getCauses());
}
}
@@ -0,0 +1,140 @@
/*
* The MIT License
*
* Copyright 2014-2015 Christopher Suarez, Oleg Nenashev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.job;

import hudson.Extension;
import hudson.model.User;
import hudson.security.SecurityRealm;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import jenkins.model.Jenkins;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.UserDetails;
import org.kohsuke.stapler.DataBoundConstructor;

import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.Messages;
import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.JobRestriction;
import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.JobRestrictionDescriptor;
import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.util.GroupSelector;

/**
* {@link JobRestriction}, which checks if the user belongs to the specified groups.
*
* @author Christopher Suarez
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
* @since 0.4
*/
public class StartedByMemberOfGroupRestriction extends AbstractUserCauseRestriction {

private final List<GroupSelector> groupList;

private transient Set<String> acceptedGroups = null;

@DataBoundConstructor
public StartedByMemberOfGroupRestriction(List<GroupSelector> groupList,
boolean checkUpstreamProjects) {
super(checkUpstreamProjects);
this.groupList = groupList;
}

public List<GroupSelector> getGroupList() {
return groupList;
}

private synchronized @Nonnull
Set<String> getAcceptedGroups() {
if (acceptedGroups == null) {
final List<GroupSelector> selectors = getGroupList();
acceptedGroups = new HashSet<String>(selectors.size());
for (GroupSelector selector : selectors) {
// merge equal entries
acceptedGroups.add(selector.getSelectedGroupId());
}
}
return acceptedGroups;
}

@Override
protected boolean acceptsUser(@CheckForNull String userId) {
if (userId == null) {
return false;
}
final @CheckForNull List<String> authorities = getAuthorities(userId);
if (authorities == null) {
return false;
}

final Set<String> allowedGroups = getAcceptedGroups();
for (String groupId : authorities) {
if (allowedGroups.contains(groupId)) {
return true;
}
}
return false;
}

private List<String> getAuthorities(String userId) {
final @CheckForNull User usr = User.get(userId, false, null);
if (usr == null) {
return getAuthoritiesFromRealm(userId);
}
List<String> authorities = usr.getAuthorities();
if (authorities == null || authorities.size() == 0) {
return getAuthoritiesFromRealm(userId);
}
return authorities;
}

private List<String> getAuthoritiesFromRealm(String userId) {
SecurityRealm sr = Jenkins.getInstance().getSecurityRealm();
final @CheckForNull UserDetails userDetails = sr.loadUserByUsername(userId);
if (userDetails == null) {
return null;
}
GrantedAuthority[] authorities = userDetails.getAuthorities();
List<String> authorityList = new ArrayList<String>(authorities.length);

for (GrantedAuthority auth : authorities) {
authorityList.add(auth.getAuthority());
}
return authorityList;
}

@Extension
public static class DescriptorImpl extends JobRestrictionDescriptor {

@Override
public String getDisplayName() {
return Messages.restrictions_Job_StartedByMemberOfGroupRestriction_displayName();
}
}
}
@@ -0,0 +1,101 @@
/*
* The MIT License
*
* Copyright 2014 Oleg Nenashev <o.v.nenashev@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.job;

import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.Messages;
import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.JobRestrictionDescriptor;
import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.util.UserSelector;
import hudson.Extension;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Handles restrictions from User causes.
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
* @since 0.4
*/
public class StartedByUserRestriction extends AbstractUserCauseRestriction {

private final List<UserSelector> usersList;

/**
* @deprecated Not implemented
*/
private final @Deprecated boolean acceptAutomaticRuns;
private final boolean acceptAnonymousUsers;
private transient Set<String> acceptedUsers = null;

@DataBoundConstructor
public StartedByUserRestriction(List<UserSelector> usersList, boolean checkUpstreamProjects,
boolean acceptAutomaticRuns, boolean acceptAnonymousUsers) {
super(checkUpstreamProjects);
this.usersList = usersList;
this.acceptAutomaticRuns = acceptAutomaticRuns;
this.acceptAnonymousUsers = acceptAnonymousUsers;
}

public List<UserSelector> getUsersList() {
return usersList;
}

public @Deprecated boolean isAcceptAutomaticRuns() {
return acceptAutomaticRuns;
}

public boolean isAcceptAnonymousUsers() {
return acceptAnonymousUsers;
}

private synchronized @Nonnull Set<String> getAcceptedUsers() {
if (acceptedUsers == null) {
final List<UserSelector> selectors = getUsersList();
acceptedUsers = new HashSet<String>(selectors.size());
for (UserSelector selector : selectors) {
acceptedUsers.add(selector.getSelectedUserId()); // merge equal entries
}
}
return acceptedUsers;
}

@Override
protected boolean acceptsUser(@CheckForNull String userId) {
return userId == null
? acceptAnonymousUsers
: getAcceptedUsers().contains(userId);
}

@Extension
public static class DescriptorImpl extends JobRestrictionDescriptor {

@Override
public String getDisplayName() {
return Messages.restrictions_Job_StartedByUserRestriction_displayName();
}
}
}

0 comments on commit 6767337

Please sign in to comment.