-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,4 +12,3 @@ buildNumber.properties | |
# IntelliJ | ||
.idea/ | ||
*.iml | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package com.github.lernejo.korekto.toolkit.thirdparty.github | ||
|
||
import io.jsonwebtoken.JwtBuilder | ||
import io.jsonwebtoken.Jwts | ||
import io.jsonwebtoken.security.SecureDigestAlgorithm | ||
import org.bouncycastle.jce.provider.BouncyCastleProvider | ||
import org.eclipse.jgit.api.GitCommand | ||
import org.eclipse.jgit.api.TransportCommand | ||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider | ||
import org.kohsuke.github.GitHubBuilder | ||
import org.testcontainers.shaded.org.bouncycastle.openssl.PEMKeyPair | ||
import org.testcontainers.shaded.org.bouncycastle.openssl.PEMParser | ||
import org.testcontainers.shaded.org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter | ||
import java.io.File | ||
import java.io.FileReader | ||
import java.net.HttpURLConnection | ||
import java.security.PrivateKey | ||
import java.security.PublicKey | ||
import java.security.Security | ||
import java.time.Instant | ||
import java.util.* | ||
|
||
object GitHubAuthenticationHolder { | ||
val auth: GitHubAuthentication by lazy { | ||
val token = System.getProperty("github_token") | ||
val appId = System.getProperty("github_app_id") | ||
val appPk = System.getProperty("github_app_pk") | ||
if (token != null) { | ||
TokenGitHubAuthentication(token) | ||
} else if (appId != null && appPk != null) { | ||
AppGitHubAuthentication(appId, appPk) | ||
Check warning on line 31 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L31
|
||
} else { | ||
NoopTokenGitHubAuthentication() | ||
Check warning on line 33 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L33
|
||
} | ||
} | ||
} | ||
|
||
interface GitHubAuthentication { | ||
val type: String | ||
fun <R, C : GitCommand<R>> configure(command: TransportCommand<in C, R>) | ||
fun configure(conn: HttpURLConnection) | ||
fun configure(builder: GitHubBuilder) | ||
} | ||
|
||
class NoopTokenGitHubAuthentication : GitHubAuthentication { | ||
override val type = "noop" | ||
Check warning on line 46 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L45-L46
|
||
|
||
override fun <R, C : GitCommand<R>> configure(command: TransportCommand<in C, R>) { | ||
} | ||
Check warning on line 49 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L49
|
||
|
||
override fun configure(conn: HttpURLConnection) { | ||
} | ||
Check warning on line 52 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L52
|
||
|
||
override fun configure(builder: GitHubBuilder) { | ||
} | ||
Check warning on line 55 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L55
|
||
} | ||
|
||
data class TokenGitHubAuthentication(val token: String) : GitHubAuthentication { | ||
override val type = "token" | ||
|
||
override fun <R, C : GitCommand<R>> configure(command: TransportCommand<in C, R>) { | ||
command | ||
.setCredentialsProvider(UsernamePasswordCredentialsProvider(token, "")) | ||
} | ||
|
||
override fun configure(conn: HttpURLConnection) { | ||
conn.setRequestProperty("Authorization", "token $token") | ||
} | ||
|
||
override fun configure(builder: GitHubBuilder) { | ||
builder.withOAuthToken(token) | ||
} | ||
} | ||
|
||
class AppGitHubAuthentication(private val appId: String, appPk: String) : GitHubAuthentication { | ||
override val type = "app-$appId (installation: " + getToken().installationId + ")" | ||
Check warning on line 76 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L75-L76
|
||
|
||
companion object { | ||
init { | ||
Security.removeProvider("BC") //remove old/legacy Android-provided BC provider | ||
Security.addProvider(BouncyCastleProvider()) // add 'real'/correct BC provider | ||
} | ||
Check warning on line 82 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L79-L82
|
||
} | ||
|
||
private val privateKey = readPrivateKey(appPk) | ||
Check warning on line 85 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L85
|
||
private var jwt: Jwt? = null | ||
private val tokensByUser: MutableMap<String, InstallationToken> = mutableMapOf() | ||
Check warning on line 87 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L87
|
||
|
||
override fun <R, C : GitCommand<R>> configure(command: TransportCommand<in C, R>) { | ||
command.setCredentialsProvider(UsernamePasswordCredentialsProvider("x-access-token", getToken().token)) | ||
} | ||
Check warning on line 91 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L90-L91
|
||
|
||
override fun configure(conn: HttpURLConnection) { | ||
val token = getToken().token | ||
conn.setRequestProperty("Authorization", "Bearer $token") | ||
} | ||
Check warning on line 96 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L94-L96
|
||
|
||
override fun configure(builder: GitHubBuilder) { | ||
builder.withAppInstallationToken(getToken().token) | ||
} | ||
Check warning on line 100 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L99-L100
|
||
|
||
private fun getToken(): InstallationToken { | ||
val user = System.getProperty("github_user") ?: throw IllegalStateException("Missing github_user env prop") | ||
val token = tokensByUser[user] | ||
Check warning on line 104 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L104
|
||
if (token == null || token.isExpired()) { | ||
tokensByUser[user] = refreshToken(user) | ||
Check warning on line 106 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L106
|
||
} | ||
return tokensByUser[user]!! | ||
Check warning on line 108 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L108
|
||
} | ||
|
||
private fun refreshToken(user: String): InstallationToken { | ||
val jwt = getJwt() | ||
val gitHubApp = GitHubBuilder().withJwtToken(jwt).build() | ||
val installation = gitHubApp.app.getInstallationByUser(user) | ||
Check warning on line 114 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L112-L114
|
||
|
||
val tokenResponse = installation.createToken().create() | ||
Check warning on line 116 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L116
|
||
|
||
return InstallationToken(tokenResponse.token, installation.id, tokenResponse.expiresAt.toInstant()) | ||
Check warning on line 118 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L118
|
||
} | ||
|
||
private fun getJwt(): String { | ||
if (jwt == null || jwt!!.isExpired()) { | ||
jwt = createJWT(appId, 590000, privateKey) | ||
Check warning on line 123 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L123
|
||
} | ||
return jwt!!.token | ||
Check warning on line 125 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L125
|
||
} | ||
} | ||
|
||
class Jwt(val token: String, private val start: Long, private val duration: Long) { | ||
Check warning on line 129 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L129
|
||
fun isExpired() = (start + duration - 10_000) > System.currentTimeMillis() | ||
} | ||
|
||
class InstallationToken(val token: String, val installationId: Long, private val expiresAt: Instant) { | ||
fun isExpired() = expiresAt.isAfter(Instant.now().minusSeconds(60L)) | ||
Check warning on line 134 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L133-L134
|
||
} | ||
|
||
fun readPrivateKey(filename: String): PrivateKey { | ||
val pemParser = PEMParser(FileReader(File(filename))) | ||
val o: PEMKeyPair = pemParser.readObject() as PEMKeyPair | ||
val converter = JcaPEMKeyConverter().setProvider("BC") | ||
val kp = converter.getKeyPair(o) | ||
return kp.private | ||
Check warning on line 142 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L138-L142
|
||
} | ||
|
||
fun createJWT(githubAppId: String, ttlMillis: Long, privateKey: PrivateKey): Jwt { | ||
//The JWT signature algorithm we will be using to sign the token | ||
val signatureAlgorithm: SecureDigestAlgorithm<PrivateKey, PublicKey> = Jwts.SIG.RS256 | ||
Check warning on line 147 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L147
|
||
|
||
val nowMillis = System.currentTimeMillis() | ||
val now = Date(nowMillis) | ||
Check warning on line 150 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L149-L150
|
||
|
||
//Let's set the JWT Claims | ||
val builder: JwtBuilder = Jwts.builder() | ||
.issuedAt(now) | ||
.issuer(githubAppId) | ||
.signWith(privateKey, signatureAlgorithm) | ||
Check warning on line 156 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L153-L156
|
||
|
||
//if it has been specified, let's add the expiration | ||
if (ttlMillis > 0) { | ||
val expMillis = nowMillis + ttlMillis | ||
val exp = Date(expMillis) | ||
builder.expiration(exp) | ||
Check warning on line 162 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L160-L162
|
||
} | ||
|
||
//Builds the JWT and serializes it to a compact, URL-safe string | ||
return Jwt(builder.compact(), nowMillis, ttlMillis) | ||
Check warning on line 166 in src/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt Codecov / codecov/patchsrc/main/kotlin/com/github/lernejo/korekto/toolkit/thirdparty/github/GitHubAuthenticationHolder.kt#L166
|
||
} |