Created
August 25, 2020 18:02
-
-
Save daper/492cefc39e95579b0fbd771a57775666 to your computer and use it in GitHub Desktop.
withGitHubAPI: Simple GitHub Api Builder Wrapper for Jenkins with GitHub App JWT Authentication
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env groovy | |
import java.util.Base64 | |
import java.security.Key | |
import java.security.PrivateKey | |
import java.security.KeyFactory | |
import java.security.spec.KeySpec | |
import java.security.spec.PKCS8EncodedKeySpec | |
import org.kohsuke.github.GHApp | |
import org.kohsuke.github.GitHub | |
import org.kohsuke.github.GHRepository | |
import org.kohsuke.github.GitHubBuilder | |
import org.kohsuke.github.GHOrganization | |
import org.kohsuke.github.GHAppInstallation | |
import org.kohsuke.github.GHAppInstallationToken | |
import io.jsonwebtoken.Jwts | |
import io.jsonwebtoken.JwtBuilder | |
import io.jsonwebtoken.SignatureAlgorithm | |
class GitHubAPIWrapper implements java.io.Serializable { | |
private String appId | |
private String privKeyB64 | |
private GHAppInstallationToken installationToken | |
GitHubAPIWrapper(String appId, String privKeyB64) { | |
this.appId = appId | |
this.privKeyB64 = privKeyB64 | |
} | |
private PrivateKey getKey() throws Exception { | |
Base64.Decoder b64Decoder = Base64.getDecoder() | |
byte[] decoded = b64Decoder.decode(this.privKeyB64) | |
KeySpec spec = new PKCS8EncodedKeySpec(decoded) | |
KeyFactory kf = KeyFactory.getInstance("RSA") | |
return kf.generatePrivate(spec) | |
} | |
private String createJWT(String githubAppId, long ttlMillis) throws Exception { | |
//The JWT signature algorithm we will be using to sign the token | |
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256 | |
long nowMillis = System.currentTimeMillis() | |
Date now = new Date(nowMillis) | |
//We will sign our JWT with our private key | |
Key signingKey = this.getKey() | |
//Let's set the JWT Claims | |
JwtBuilder builder = Jwts.builder() | |
.setIssuedAt(now) | |
.setIssuer(githubAppId) | |
.signWith(signingKey, signatureAlgorithm) | |
//if it has been specified, let's add the expiration | |
if (ttlMillis > 0) { | |
long expMillis = nowMillis + ttlMillis | |
Date exp = new Date(expMillis) | |
builder.setExpiration(exp) | |
} | |
//Builds the JWT and serializes it to a compact, URL-safe string | |
return builder.compact() | |
} | |
private GHAppInstallationToken createInstallationToken(String appId, long ttlMillis = 600000 - (6000 * 8)) { | |
// sometimes time differ, so let's give it a margin | |
String jwtToken = createJWT(appId, ttlMillis) | |
GitHub github = new GitHubBuilder().withJwtToken(jwtToken).build() | |
GHApp app = github.getApp() | |
List<GHAppInstallation> appInstallations = app.listInstallations().asList() | |
GHAppInstallation appInstallation = appInstallations.get(0) | |
GHAppInstallationToken appInstallationToken = appInstallation | |
.createToken(appInstallation.getPermissions()) | |
.create() | |
return appInstallationToken | |
} | |
GitHub build() throws Exception { | |
if (this.installationToken == null) { | |
this.installationToken = createInstallationToken(this.appId) | |
} | |
GitHub github = new GitHubBuilder() | |
.withAppInstallationToken(this.installationToken.getToken()) | |
.build() | |
try { | |
github.checkApiUrlValidity() | |
return github | |
} catch(Exception e) { | |
this.installationToken = createInstallationToken(this.appId) | |
} | |
github = new GitHubBuilder() | |
.withAppInstallationToken(this.installationToken.getToken()) | |
.build() | |
return github | |
} | |
} | |
/* | |
* @description: Accepts a closure which delegates the GitHub API as github | |
* @param: Closure closure | |
*/ | |
void withGitHubAPI(Map settings, Closure closure) throws Exception { | |
if (! settings.containsKey('appId') || ! settings.containsKey('privKeyB64')) { | |
throw new Exception('appId and/or privKey are missing for withGitHubAPI') | |
} | |
GitHubAPIWrapper gitHubAPIWrapper = new GitHubAPIWrapper(settings.appId, settings.privKeyB64) | |
GitHub github = gitHubAPIWrapper.build() | |
closure.delegate = [github: github] | |
closure.call() | |
} | |
return withGitHubAPI | |
// | |
// Example of usage | |
// | |
// node { | |
// stage('Stage') { | |
// withGitHubAPI = load 'path/to/this/file.groovy' | |
// withGitHubAPI([ | |
// appId: GITHUB_APP_ID, | |
// privKeyB64: PRIV_KEY.trim().replace("\n", "") | |
// ]) { | |
// def GHOrganization org = github.getOrganization('org') | |
// def Map<GHRepository> repos = org.getRepositories() | |
// def repos = repos.findAll { _, repo -> | |
// def name = repo.getName() | |
// return name ==~ /^my-repo-slug-.*/ | |
// } | |
// println('Repos that match my-repo-slug-*:') | |
// println(repos.collect{ _, repo -> repo.getName() }) | |
// def repo = org.getRepository('repo') | |
// println('Repo branches:') | |
// println(repo.getBranches().collect{ _, branch -> branch.getName() }) | |
// } | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment