44
55package io.gitpod.toolbox.auth
66
7- import com.connectrpc.Code
8- import com.connectrpc.ConnectException
97import com.jetbrains.toolbox.api.core.auth.*
10- import io.gitpod.publicapi.experimental.v1.UserServiceClient
11- import io.gitpod.toolbox.service.GitpodPublicApiManager
8+ import io.gitpod.toolbox.service.GitpodPublicApi
129import io.gitpod.toolbox.service.Utils
1310import kotlinx.coroutines.future.future
14- import kotlinx.serialization.Serializable
15- import kotlinx.serialization.encodeToString
16- import kotlinx.serialization.json.Json
17- import kotlinx.serialization.json.jsonObject
18- import kotlinx.serialization.json.jsonPrimitive
1911import java.net.URI
20- import java.util.*
2112import java.util.concurrent.Future
2213
23- // TODO(hw): Validate Scopes
24- val authScopesJetBrainsToolbox = listOf (
25- " function:getGitpodTokenScopes" ,
26- " function:getLoggedInUser" ,
27- " function:getOwnerToken" ,
28- " function:getWorkspace" ,
29- " function:getWorkspaces" ,
30- " function:listenForWorkspaceInstanceUpdates" ,
31- " function:startWorkspace" ,
32- " function:stopWorkspace" ,
33- " function:deleteWorkspace" ,
34- " function:getToken" ,
35- " resource:default" ,
36- )
37-
3814class GitpodAuthManager {
39- private val manager: PluginAuthManager <GitpodAccount , GitpodLoginConfiguration >
15+ val manager: PluginAuthManager <GitpodAccount , GitpodLoginConfiguration >
4016 private var loginListeners: MutableList < () -> Unit > = mutableListOf ()
4117 private var logoutListeners: MutableList < () -> Unit > = mutableListOf ()
4218
19+
4320 init {
4421 manager = Utils .sharedServiceLocator.getAuthManager(
4522 " gitpod" ,
4623 GitpodAccount ::class .java,
4724 { it.encode() },
4825 { GitpodAccount .decode(it) },
4926 { oauthToken, authCfg -> getAuthenticatedUser(URI .create(authCfg.baseUrl).host, oauthToken) },
50- { oauthToken, gpAccount -> getAuthenticatedUser(gpAccount.getHost (), oauthToken) },
27+ { oauthToken, gpAccount -> getAuthenticatedUser(gpAccount.getGitpodHost (), oauthToken) },
5128 { gpLoginCfg ->
5229 val authParams = mapOf (
5330 " response_type" to " code" ,
@@ -90,43 +67,32 @@ class GitpodAuthManager {
9067
9168 private fun resetCurrentAccount (accountId : String ) {
9269 val account = manager.accountsWithStatus.find { it.account.id == accountId }?.account ? : return
93- Utils .logger.debug(" reset settings for ${account.getHost()} " )
94- Utils .gitpodSettings.resetSettings(account.getHost())
70+ Utils .logger.debug(" reset settings for ${account.getGitpodHost()} " )
71+ Utils .gitpodSettings.resetSettings(account.getGitpodHost())
72+ }
73+
74+ fun logoutCurrentAccount () {
75+ getCurrentAccount()?.let { manager.logout(it.id) }
9576 }
9677
9778 fun getCurrentAccount (): GitpodAccount ? {
98- return manager.accountsWithStatus.find { it.account.getHost () == Utils .gitpodSettings.gitpodHost }?.account
79+ return manager.accountsWithStatus.find { it.account.getGitpodHost () == Utils .gitpodSettings.gitpodHost }?.account
9980 }
10081
101- suspend fun loginWithHost (host : String ): Boolean {
102- val currentAccount = getCurrentAccount()
103- if (currentAccount?.getHost() == host) {
104- if (currentAccount.isValidate()) {
105- return true
106- } else {
107- manager.logout(currentAccount.id)
108- Utils .openUrl(this .getOAuthLoginUrl(host))
109- return false
110- }
111- }
112- val account = manager.accountsWithStatus.find { it.account.getHost() == host }?.account
113- if (account != null ) {
114- if (account.isValidate()) {
115- Utils .gitpodSettings.gitpodHost = host
116- loginListeners.forEach { it() }
117- return true
118- } else {
119- manager.logout(account.id)
120- Utils .openUrl(this .getOAuthLoginUrl(host))
82+ fun loginWithHost (host : String ): Boolean {
83+ val account = manager.accountsWithStatus.find {
84+ if (it.expired) {
85+ manager.logout(it.account.id)
12186 return false
12287 }
123- }
124- Utils .openUrl(this .getOAuthLoginUrl(host))
125- return false
126- }
88+ return it.account.getGitpodHost() == host
89+ }?.account
12790
128- fun logout () {
129- getCurrentAccount()?.let { manager.logout(it.id) }
91+ if (account == null ) {
92+ Utils .openUrl(getOAuthLoginUrl(host))
93+ return false
94+ }
95+ return true // getFromCache
13096 }
13197
13298 fun getOAuthLoginUrl (gitpodHost : String ): String {
@@ -153,81 +119,9 @@ class GitpodAuthManager {
153119
154120 private fun getAuthenticatedUser (gitpodHost : String , oAuthToken : OAuthToken ): Future <GitpodAccount > {
155121 return Utils .coroutineScope.future {
156- val bearerToken = getBearerToken(oAuthToken)
157- val client = GitpodPublicApiManager .createClient(gitpodHost, bearerToken)
158- val user = GitpodPublicApiManager .tryGetAuthenticatedUser(UserServiceClient (client))
159- GitpodAccount (bearerToken, user.id, user.name, gitpodHost, authScopesJetBrainsToolbox)
160- }
161- }
162-
163- private fun getBearerToken (oAuthToken : OAuthToken ): String {
164- val parts = oAuthToken.authorizationHeader.replace(" Bearer " , " " ).split(" ." )
165- // We don't validate jwt token
166- if (parts.size != 3 ) {
167- throw IllegalArgumentException (" Invalid JWT" )
168- }
169- val decoded = String (Base64 .getUrlDecoder().decode(parts[1 ].toByteArray()))
170- val jsonElement = Json .parseToJsonElement(decoded)
171- val payloadMap = jsonElement.jsonObject.mapValues {
172- it.value.jsonPrimitive.content
173- }
174- return payloadMap[" jti" ] ? : throw IllegalArgumentException (" Failed to parse JWT token" )
175- }
176-
177- }
178-
179- class GitpodLoginConfiguration (val hostUrl : String )
180-
181- @Serializable
182- class GitpodAccount : Account {
183- private val credentials: String
184- private val id: String
185- private val name: String
186- private val host: String
187- private val scopes: List <String >
188-
189- constructor (credentials: String , id: String , name: String , host: String , scopes: List <String >) {
190- this .credentials = credentials
191- this .id = id
192- this .name = name
193- this .host = host
194- this .scopes = scopes
195- }
196-
197- override fun getId () = id
198- override fun getFullName () = name
199- fun getCredentials () = credentials
200- fun getHost () = host
201- fun getScopes () = scopes
202-
203- fun encode (): String {
204- return Json .encodeToString(this )
205- }
206-
207- suspend fun isValidate (): Boolean {
208- val hostUrl = " https://$host "
209- val client = GitpodPublicApiManager .createClient(URI (hostUrl).host, credentials)
210- Utils .logger.debug(" validating account $hostUrl " )
211- try {
212- GitpodPublicApiManager .tryGetAuthenticatedUser(UserServiceClient (client))
213- // TODO: Verify scopes
214- return true
215- } catch (e: ConnectException ) {
216- // TODO(hw): Server close jsonrpc so papi server respond internal error
217- if (e.code == Code .UNAUTHENTICATED || (e.code == Code .INTERNAL_ERROR && e.message != null && e.message!! .contains(
218- " jsonrpc2: connection is closed"
219- ))
220- ) {
221- Utils .logger.error(" account $hostUrl is not valid" )
222- return false
223- }
224- return true
225- }
226- }
227-
228- companion object {
229- fun decode (str : String ): GitpodAccount {
230- return Json .decodeFromString<GitpodAccount >(str)
122+ val apiClient = GitpodPublicApi (gitpodHost, { Utils .coroutineScope.future { oAuthToken } }) {_, _ -> }
123+ val user = apiClient.getAuthenticatedUser()
124+ GitpodAccount (user.id, user.name, gitpodHost, authScopesJetBrainsToolbox)
231125 }
232126 }
233127}
0 commit comments