All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.delta.sharing.client.auth.OAuthClientCredentialsAuthProvider.scala Maven / Gradle / Ivy

/*
 * Copyright (2021) The Delta Lake Project Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.delta.sharing.client.auth

import java.util.concurrent.locks.ReentrantReadWriteLock

import org.apache.http.HttpHeaders
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.impl.client.CloseableHttpClient

import io.delta.sharing.client.OAuthClientCredentialsDeltaSharingProfile

private[client] case class OAuthClientCredentialsAuthProvider(
  client: CloseableHttpClient,
  authConfig: AuthConfig,
  profile: OAuthClientCredentialsDeltaSharingProfile) extends AuthCredentialProvider {

  private val readWriteLock = new ReentrantReadWriteLock()
  private val readLock = readWriteLock.readLock
  private val writeLock = readWriteLock.writeLock

  private[auth] lazy val oauthClient = new OAuthClient(client, authConfig,
    profile.tokenEndpoint, profile.clientId, profile.clientSecret, profile.scope)

  // this can be updated on different thread
  // read has be through readLock and write has to be through writeLock
  private var currentToken: Option[OAuthClientCredentials] = None

  override def addAuthHeader(httpRequest: HttpRequestBase): Unit = {
    val token = maybeRefreshToken()

    readLock.lock()
    try {
      httpRequest.setHeader(HttpHeaders.AUTHORIZATION, s"Bearer ${token.accessToken}")
    } finally {
      readLock.unlock()
    }
  }

  // Method to set the current token for testing purposes
  private[auth] def setCurrentTokenForTesting(token: OAuthClientCredentials): Unit = {
    writeLock.lock()
    try {
      currentToken = Some(token)
    } finally {
      writeLock.unlock()
    }
  }

  private def maybeRefreshToken(): OAuthClientCredentials = {
    readLock.lock()
    try {
      if (currentToken.isDefined && !needsRefresh(currentToken.get)) {
        return currentToken.get
      }
    } finally {
      readLock.unlock()
    }

    writeLock.lock()
    try {
      if (currentToken.isEmpty || needsRefresh(currentToken.get)) {
        val newToken = oauthClient.clientCredentials()
        currentToken = Some(newToken)
      }

      currentToken.get
    } finally {
      writeLock.unlock()
    }
  }

  private[auth] def needsRefresh(token: OAuthClientCredentials): Boolean = {
    val now = System.currentTimeMillis()
    val expirationTime = token.creationTimestamp + token.expiresIn * 1000
    expirationTime - now < authConfig.tokenRenewalThresholdInSeconds * 1000
  }

  override def getExpirationTime(): Option[String] = None
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy