-
Notifications
You must be signed in to change notification settings - Fork 168
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
[JENKINS-49486] Enable variable expansion for ManualCondition.users (approvers) #114
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,8 @@ | |
import java.util.concurrent.Future; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import javax.annotation.CheckForNull; | ||
import javax.annotation.Nonnull; | ||
|
||
|
@@ -54,6 +56,7 @@ | |
public class ManualCondition extends PromotionCondition { | ||
private String users; | ||
private List<ParameterDefinition> parameterDefinitions = new ArrayList<ParameterDefinition>(); | ||
private static final Logger LOGGER = Logger.getLogger(ManualCondition.class.getName()); | ||
|
||
/** | ||
* | ||
|
@@ -96,17 +99,31 @@ public ParameterDefinition getParameterDefinition(String name) { | |
} | ||
|
||
public Set<String> getUsersAsSet() { | ||
if (users == null || users.equals("")) | ||
return getUsersAsSet(null); | ||
} | ||
|
||
public Set<String> getUsersAsSet(AbstractBuild<?,?> build) { | ||
if (users == null || users.equals("") || build == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is not correct from what I see. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What sensitive environment variable is exposing right now? |
||
return Collections.emptySet(); | ||
|
||
Set<String> set = new HashSet<String>(); | ||
for (String user : users.split(",")) { | ||
user = user.trim(); | ||
|
||
if (user.trim().length() > 0) | ||
LOGGER.log(Level.FINER, "ManualCondition.getUsersAsSet(): user={0}", user); | ||
if (user.startsWith("$")){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You still need to null-check build somewhere before using it below. I think it makes sense to do it here: |
||
user = user.substring(1); | ||
if (user.startsWith("{") && user.endsWith("}")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to allow either There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reason I allow both But if you insist, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If email-ext and token-macro support both, then it probably makes sense to support both here as well. I'd leave it the way you have it for now. |
||
user = user.substring(1, user.length() -1); | ||
LOGGER.log(Level.INFO, "ManualCondition.getUsersAsSet(): build.getBuildVariableResolver().resolve(user=${0})={1}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
new Object [] {user, build.getBuildVariableResolver().resolve(user)}); | ||
user = build.getBuildVariableResolver().resolve(user); | ||
} | ||
if (user != null && user.trim().length() > 0) | ||
set.add(user); | ||
} | ||
|
||
LOGGER.log(Level.INFO, "ManualCondition.getUsersAsSet(): return set={0}", set); | ||
return set; | ||
} | ||
|
||
|
@@ -129,7 +146,7 @@ public PromotionBadge isMet(PromotionProcess promotionProcess, AbstractBuild<?,? | |
* approved. | ||
*/ | ||
public boolean canApprove(PromotionProcess promotionProcess, AbstractBuild<?,?> build) { | ||
if (!PromotionPermissionHelper.hasPermission(build.getProject(), this)) { | ||
if (!PromotionPermissionHelper.hasPermission(build.getProject(), build, this)) { | ||
return false; | ||
} | ||
|
||
|
@@ -150,16 +167,24 @@ public boolean canApprove(PromotionProcess promotionProcess, AbstractBuild<?,?> | |
* @since 2.24 | ||
*/ | ||
public boolean isInUsersList() { | ||
return isInUsersList(null); | ||
} | ||
|
||
public boolean isInUsersList(AbstractBuild<?,?> build) { | ||
// Current user must be in users list or users list is empty | ||
Set<String> usersSet = getUsersAsSet(); | ||
Set<String> usersSet = getUsersAsSet(build); | ||
return usersSet.contains(Hudson.getAuthentication().getName()); | ||
} | ||
|
||
/* | ||
* Check if user is a member of a groups as listed in the user / group field | ||
*/ | ||
public boolean isInGroupList() { | ||
Set<String> groups = getUsersAsSet(); | ||
return isInGroupList(null); | ||
} | ||
|
||
public boolean isInGroupList(AbstractBuild<?,?> build) { | ||
Set<String> groups = getUsersAsSet(build); | ||
GrantedAuthority[] authorities = Hudson.getAuthentication().getAuthorities(); | ||
for (GrantedAuthority authority : authorities) { | ||
if (groups.contains(authority.getAuthority())) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,17 @@ | |
|
||
import hudson.ExtensionList; | ||
import hudson.model.AbstractProject; | ||
import hudson.model.Cause; | ||
import hudson.model.FreeStyleBuild; | ||
import hudson.model.Item; | ||
import hudson.model.ParametersAction; | ||
import hudson.model.ParameterDefinition; | ||
import hudson.model.ParameterValue; | ||
import hudson.model.Result; | ||
import hudson.model.Descriptor; | ||
import hudson.model.FreeStyleProject; | ||
import hudson.model.StringParameterDefinition; | ||
import hudson.model.StringParameterValue; | ||
import hudson.model.User; | ||
import hudson.plugins.promoted_builds.JobPropertyImpl; | ||
import hudson.plugins.promoted_builds.PromotedBuildAction; | ||
|
@@ -263,6 +266,25 @@ public void testManualPromotionPermissions() throws Exception { | |
approval, notNullValue()); | ||
} | ||
|
||
{ | ||
// Approvers specified in varaible, user is approver, but does not have Promotion/Promote | ||
cond.setUsers("${approver}"); | ||
List<ParameterValue> paramsList = new ArrayList<ParameterValue>(); | ||
paramsList.add(new StringParameterValue("approver", "non-promoter")); | ||
//FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); | ||
FreeStyleBuild b = j.assertBuildStatusSuccess( | ||
p.scheduleBuild2( | ||
0, | ||
new Cause.RemoteCause("test-host", ""), | ||
new ParametersAction(paramsList))); | ||
SecurityContext previous = ACL.impersonate(User.get("non-promoter").impersonate()); | ||
j.assertBuildStatusSuccess(cond.approve(b, pp, Collections.EMPTY_LIST)); | ||
ACL.impersonate(previous.getAuthentication()); | ||
ManualApproval approval = b.getAction(ManualApproval.class); | ||
assertThat("If users are specified in variable, then users in that list should be able to approve even without Promotion/Promote permissions", | ||
approval, notNullValue()); | ||
} | ||
|
||
{ | ||
// Approvers specified, user is not approver, but does have Promotion/Promote | ||
cond.setUsers("non-promoter"); | ||
|
@@ -283,7 +305,14 @@ public void testManualPromotionPermissionsViaWebClient() throws Exception { | |
PromotionProcess pp = addPromotionProcess(p, "foo"); | ||
WebClient wc = j.createWebClient(); | ||
|
||
FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); | ||
List<ParameterValue> paramsList = new ArrayList<ParameterValue>(); | ||
paramsList.add(new StringParameterValue("approver", "promoter")); | ||
//FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); | ||
FreeStyleBuild b = j.assertBuildStatusSuccess( | ||
p.scheduleBuild2( | ||
0, | ||
new Cause.RemoteCause("test-host", ""), | ||
new ParametersAction(paramsList))); | ||
ManualCondition cond = new ManualCondition(); | ||
pp.conditions.add(cond); | ||
j.assertBuildStatusSuccess(cond.approve(b, pp, Collections.EMPTY_LIST)); | ||
|
@@ -322,13 +351,26 @@ public void testManualPromotionPermissionsViaWebClient() throws Exception { | |
assertThat(waitForBuildByNumber(pp, 3).getResult(), equalTo(Result.SUCCESS)); | ||
} | ||
|
||
{ | ||
// Re-execute promotion as specified user in varaible without Promotion/Promote | ||
cond.setUsers("${approver}"); | ||
wc.login("non-promoter", "non-promoter"); | ||
try { | ||
wc.getPage(b, String.format("promotion/%s/build?json={}", pp.getName())); | ||
fail(); | ||
} catch (FailingHttpStatusCodeException e) { | ||
assertThat(e.getStatusCode(), equalTo(404)); // Redirect after the build is broken. | ||
} | ||
assertThat(waitForBuildByNumber(pp, 4).getResult(), equalTo(Result.SUCCESS)); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dwnusbaum There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At a glance, no, but you could use a debugger and step through the test/plugin code to figure out why it is failing before removing it :) |
||
{ | ||
// Re-execute promotion as unspecified user with Promotion/Promote | ||
cond.setUsers("non-promoter"); | ||
wc.login("promoter", "promoter"); | ||
// Status#doBuild does a bare `return;` without scheduling the build in this case, which is why we use goTo with "" for the MIME type. | ||
wc.goTo(String.format("job/%s/%d/promotion/%s/build?json={}", p.getName(), b.getNumber(), pp.getName()), ""); | ||
assertThat(pp.getBuildByNumber(4), nullValue()); | ||
assertThat(pp.getBuildByNumber(5), nullValue()); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaks binary compatibility, although I don't see any other callers based on a GitHub search of the jenkinsci organization, so it might not matter. We could always be safe and keep the previous method around as well, and have it call the new method passing
null
for the new parameter.Same issue with
isInUsersList
andisInGroupList
.