Search code examples
javarestjenkinshttp-postjenkins-cli

JENKINS REST API throws 403 forbidden


Problem statement :

I have Jenkins Sever V:2.190.2 running on cloud. Permission "Logged In User can do any thing" is selected on Jenkins Config Security. So, it means user having valid user name and password can log in to the jenkin server and perform the authorised job. Basically I need to create job on Jenkin server by passing job name and jobXml.

Tried Below Option :

So far I have used "jenkinsci/java-client-api" api available on Github. This api is really good api for Jenkins related operation and I followed instructions as given on the READ.md. I created Jenkins Server instance and tried to call getJenkinVersion() and getJobs() method and both works well and returns results as expected. However, when I am going to call createJob(jobName, jobXml), this call returns 403 forbidden error from the server.

By digging little deeper in the issue, I found following :- 1. When I change the Jenkin security configuration to "Any User can do any thing" then this createJob() method works and I am able to create job. However, this option is not recommended due to security restriction. 2. When I keep Jenkin security configuration to "Logged in user can do any thing", then createJob() method does not work and returns 403 forbidden error. Here I also noticed that though I am providing correct user name and password/token, that is used to sign in, in to Jenkins server from UI to create Jenkin server instance as defined in user doc, when it hit to the method, it is logged in as "ANONYMOUS USER" in to jenkin. and I presume that this is the root cause for returning 403 error.

Below Code snippet :

**Sample 1**:
        HttpClientBuilder builder = HttpClientBuilder.create();
        JenkinsHttpClient client = new JenkinsHttpClient(uri, builder, "XXX", "XXX");
        JenkinsServer jenkins = new JenkinsServer(client);
        String sourceXML = readFile("src/main/resources/config.xml");
        System.out.println(String.format("Installed Jenkins Version >> %s", jenkins.getVersion().getLiteralVersion()));//works and gives correct result
        jenkins.createJob("test-nov1", sourceXML);

**Sample 2**:
        HttpClientBuilder builder = HttpClientBuilder.create();
        JenkinsHttpClient client = new JenkinsHttpClient(uri, addAuthentication(builder, uri, userName, passwordOrToken));
        JenkinsServer jenkins = new JenkinsServer(client);
        String sourceXML = readFile("src/main/resources/config.xml");
        System.out.println(String.format("Installed Jenkins Version >> %s", jenkins.getVersion().getLiteralVersion()));
        jenkins.createJob(null,"test-nov1", sourceXML,true);

**Sample Exception**:
    Exception in thread "main" org.apache.http.client.HttpResponseException: status code: 403, reason phrase: Forbidden
    at com.offbytwo.jenkins.client.validator.HttpResponseValidator.validateResponse(HttpResponseValidator.java:11)
    at com.offbytwo.jenkins.client.JenkinsHttpClient.post_xml(JenkinsHttpClient.java:375)
    at com.offbytwo.jenkins.JenkinsServer.createJob(JenkinsServer.java:389)
    at com.offbytwo.jenkins.JenkinsServer.createJob(JenkinsServer.java:359)
    at com.xx.OffByTwoJenkins.main(OffByTwoJenkins.java:31)

Option 2: I also tried with other option by directly calling Jenkin REST API directly using HttpUrl connection.

**Sample Code** :
public int createJob(final String username, final String password, final String jenkinsUrl, final String jobName) {
        // http://JENKINSURL//createItem?name=JOBNAME
        String jobUrl = jenkinsUrl + "/createItem?name=" + jobName;

        int responseCode = 00;
        try {
            String basicAuth = Base64.getEncoder().encodeToString((username+":"+password).getBytes(StandardCharsets.UTF_8));
            //String encoding = Base64.getEncoder().encodeToString((username+":"+password).getBytes("utf-8"));
            System.out.println(String.format("User Auth >> %s", basicAuth));
            String sourceXML = readFile("src/main/resources/config.xml");
            URL url = new URL(jobUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            //connection.setReadTimeout(10000);
            //connection.setConnectTimeout(15000);
            connection.setRequestProperty("Authorization", "Basic " + basicAuth);
            connection.setRequestProperty("Content-Type", "application/xml");
            connection.setRequestProperty("Content-Language", "en-US");
            connection.setRequestMethod("POST");
            connection.setUseCaches(false);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setInstanceFollowRedirects(false);
            OutputStream os = connection.getOutputStream();
            os.write(sourceXML.getBytes());
            os.flush();
            responseCode = connection.getResponseCode();
            BufferedReader br = new BufferedReader(new InputStreamReader((connection.getInputStream())));
            String output;
            System.out.println("Output from Server .... \n");
            while ((output = br.readLine()) != null) {
                System.out.println(output);
            }
            connection.disconnect();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return responseCode;

    }

This also returns with same error 403 forbidden.

**Exception** :
Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: <<JenkinsURL>>
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at com.xx.JenkinsJobExecutor.createJob(JenkinsJobExecutor.java:109)

I really not able to understand where I need to tweak for getting the job created. Thanks


Solution

  • The following solution worked for me even after CSRF was enabled.

    public class JenkinsJobCreate {
    public static void main(String[] args) {
    
        System.out.println("JenkinsJobsTrigger has started ###############################");
    
        String ipAddress = "http://localhost:8080/";
        String jobName = "Hello-world";
    
        String username = "admin";
        String password = "admin";
    
        System.out.println("ipAddress: " + ipAddress);
        System.out.println("jobName: " + jobName);
    
        System.out.println("username: " + username);
        System.out.println("password: " + password);
    
    
        try (JenkinsServer jenkinsServer = new JenkinsServer(new URI(ipAddress), username, password)) {
    
            // our XML file for this example
            File xmlFile = new File("src/main/resources/config.xml");
    
            // Let's get XML file as String using BufferedReader
            // FileReader uses platform's default character encoding
            // if you need to specify a different encoding, use InputStreamReader
            Reader fileReader = new FileReader(xmlFile);
            BufferedReader bufReader = new BufferedReader(fileReader);
    
            StringBuilder sb = new StringBuilder();
            String line = bufReader.readLine();
            while( line != null){
                sb.append(line).append("\n");
                line = bufReader.readLine();
            }
            String jobXml = sb.toString();
            System.out.println("XML to String using BufferedReader : ");
            System.out.println(jobXml);
    
            bufReader.close();
    
            jenkinsServer.createJob(jobName, jobXml, true);
    
        } catch (Exception e) {
            System.out.println("Exception Occured!!!");
            e.printStackTrace();
        }
    
        System.out.println("JenkinsJobsTrigger has Finished ###############################");
    
    }
    

    }

    config.xml

    <?xml version='1.1' encoding='UTF-8'?>
    <project>
      <description></description>
      <keepDependencies>false</keepDependencies>
      <properties/>
      <scm class="hudson.scm.NullSCM"/>
      <canRoam>true</canRoam>
      <disabled>false</disabled>
      <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
      <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
      <triggers/>
      <concurrentBuild>false</concurrentBuild>
      <builders>
        <hudson.tasks.Shell>
          <command>echo &quot;Jenkins Testing Hello World!&quot;</command>
        </hudson.tasks.Shell>
      </builders>
      <publishers/>
      <buildWrappers/>
    </project>