Skip to content

Commit

Permalink
Merge pull request #51 from jenkinsci/option-validate-artifact-acl
Browse files Browse the repository at this point in the history
validate artifact ACL in OptionProvider
  • Loading branch information
ltamaster committed Oct 28, 2022
2 parents 8f8d887 + dd97ee2 commit 032b3bb
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 1 deletion.
24 changes: 24 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,36 @@
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.objenesis/objenesis -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.3</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/org/jenkinsci/plugins/rundeck/OptionProvider.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.jenkinsci.plugins.rundeck;

import hudson.model.AbstractProject;
import hudson.Functions;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
Expand All @@ -21,6 +21,8 @@
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import static hudson.model.Run.ARTIFACTS;

/**
* Option provider for Rundeck - see http://rundeck.org/docs/manual/jobs.html#option-model-provider
*
Expand Down Expand Up @@ -60,6 +62,13 @@ public void doArtifact(StaplerRequest request, StaplerResponse response) throws
return;
}

try {
this.checkArtifactPermissions(build);
}catch (Exception e){
response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}

List<Option> options = new ArrayList<OptionProvider.Option>();
for (Artifact artifact : build.getArtifacts()) {
if (artifactPattern == null || artifactPattern.matcher(artifact.getFileName()).matches()) {
Expand Down Expand Up @@ -148,6 +157,7 @@ public void doBuild(StaplerRequest request, StaplerResponse response) throws IOE
RunList<?> builds = project.getBuilds();
for (Run<?, ?> build : builds) {
Artifact artifact = findArtifact(artifactName, artifactPattern, build);

if (artifact != null) {
String buildName = build.getDisplayName();
options.add(new Option(buildName, buildArtifactUrl(build, artifact)));
Expand Down Expand Up @@ -248,6 +258,12 @@ private Artifact findArtifact(String artifactName, Pattern artifactPattern, Run<
return null;
}

try{
this.checkArtifactPermissions(build);
}catch (Exception e){
return null;
}

for (Artifact artifact : build.getArtifacts()) {
if (StringUtils.equals(artifactName, artifact.getFileName())) {
return artifact;
Expand Down Expand Up @@ -290,6 +306,12 @@ private void writeJson(List<Option> options, StaplerResponse response) throws IO
response.getWriter().append(json);
}

private void checkArtifactPermissions(Run<?, ?> build){
if(Functions.isArtifactsPermissionEnabled()){
build.checkPermission(ARTIFACTS);
}
}

/**
* Javabean representation of an option
*/
Expand Down
172 changes: 172 additions & 0 deletions src/test/groovy/jenkinsci/plugins/rundeck/OptionProviderSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package jenkinsci.plugins.rundeck

import hudson.model.FreeStyleBuild
import hudson.model.Hudson
import hudson.model.Job
import hudson.model.Run
import hudson.security.AccessDeniedException2
import hudson.util.RunList
import jenkins.model.Jenkins
import org.jenkinsci.plugins.rundeck.OptionProvider
import org.kohsuke.stapler.StaplerRequest
import org.kohsuke.stapler.StaplerResponse
import spock.lang.Specification

import javax.servlet.http.HttpServletResponse

import static hudson.model.Run.ARTIFACTS

class OptionProviderSpec extends Specification {

def originalHolder

def setup() {
originalHolder = Jenkins.HOLDER
System.properties['hudson.security.ArtifactsPermission'] = "true"
}

def cleanup() {
Jenkins.HOLDER = originalHolder
}

def "test option artifact without permissions"(){

given:

final Hudson jenkins = Mock()
jenkins.getInstanceOrNull() >> jenkins
jenkins.getInstance() >> jenkins

def originalHolder = Jenkins.HOLDER

Jenkins.HOLDER = new Jenkins.JenkinsHolder() {
Jenkins getInstance() {
return jenkins
}
}

jenkins.getItemByFullName("test", _)>>Mock(Job) {
getLastSuccessfulBuild() >> Mock(FreeStyleBuild) {
checkPermission(ARTIFACTS) >> { throw new AccessDeniedException2(Jenkins.getAuthentication(), ARTIFACTS) }
}
}

def response = Mock(StaplerResponse)

when:
OptionProvider optionProvider = new OptionProvider()
def request = Mock(StaplerRequest){
getParameter("project")>>"test"
getParameter("build")>>"lastSuccessful"
}

optionProvider.doArtifact(request,response )

then:
1 * response.sendError(HttpServletResponse.SC_BAD_REQUEST, {message-> message == "anonymous is missing the Run/Artifacts permission" });


}

def "test option artifact with permissions"(){

given:

final Hudson jenkins = Mock(){
getRootUrl()>>"http://localhost:8080"
}

jenkins.getInstanceOrNull() >> jenkins
jenkins.getInstance() >> jenkins

Jenkins.HOLDER = new Jenkins.JenkinsHolder() {
Jenkins getInstance() {
return jenkins
}
}

jenkins.getItemByFullName("test", _)>>Mock(Job) {
getLastSuccessfulBuild() >> Mock(FreeStyleBuild) {
getArtifacts()>> [
Mock(Run.Artifact){
getFileName()>>"test-1.0.jar"
getHref()>>"artifact/test-1.0.jar"
}
]
getUrl()>>"/rundeck/"
}
}

def writer = Mock(PrintWriter)
def response = Mock(StaplerResponse){
getWriter()>>writer
}

when:
OptionProvider optionProvider = new OptionProvider()
def request = Mock(StaplerRequest){
getParameter("project")>>"test"
getParameter("build")>>"lastSuccessful"
}
optionProvider.doArtifact(request,response )

then:
1*writer.append({json-> json == "[{\"name\":\"test-1.0.jar\",\"value\":\"http://localhost:8080/rundeck/artifact/artifact/test-1.0.jar\"}]"})

}

def "test option build without permissions"(){

given:

final Hudson jenkins = Mock()

jenkins.getInstanceOrNull() >> jenkins
jenkins.getInstance() >> jenkins

Jenkins.HOLDER = new Jenkins.JenkinsHolder() {
Jenkins getInstance() {
return jenkins
}
}

List builds = [
Mock(FreeStyleBuild) {
getArtifacts()>> [
Mock(Run.Artifact){
getFileName()>>"test-1.0.jar"
getHref()>>"artifact/test-1.0.jar"
}
]
getUrl()>>"/rundeck/"
checkPermission(ARTIFACTS) >> { throw new AccessDeniedException2(Jenkins.getAuthentication(), ARTIFACTS) }

}
]

jenkins.getItemByFullName("test", _)>>Mock(Job) {
getBuilds()>> Mock(RunList){
iterator()>>builds.iterator()
}
}

def writer = Mock(PrintWriter)
def response = Mock(StaplerResponse){
getWriter()>>writer
}

when:
OptionProvider optionProvider = new OptionProvider()
def request = Mock(StaplerRequest){
getParameter("project")>>"test"
getParameter("artifactRegex")>>".jar"
getParameter("build")>>"lastSuccessful"
}
def result = optionProvider.doBuild(request,response )

then:
1*writer.append({json-> json == "[]"})

}

}

0 comments on commit 032b3bb

Please # to comment.