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

mill.contrib.gitlab.GitlabTokenLookup.scala Maven / Gradle / Ivy

There is a newer version: 0.12.2-20-231ff6
Show newest version
package mill.contrib.gitlab

import scala.util.Try

trait GitlabTokenLookup {
  import GitlabTokenLookup._

  // Default search places for token
  def personalTokenEnv: String = "GITLAB_PERSONAL_ACCESS_TOKEN"
  def personalTokenProperty: String = "gitlab.personal-access-token"
  def personalTokenFile: os.Path = os.home / os.RelPath(".mill/gitlab/personal-access-token")
  def personalTokenFileWD: os.RelPath = os.RelPath(".gitlab/personal-access-token")
  def deployTokenEnv: String = "GITLAB_DEPLOY_TOKEN"
  def deployTokenProperty: String = "gitlab.deploy-token"
  def deployTokenFile: os.Path = os.home / os.RelPath(".mill/gitlab/deploy-token")
  def deployTokenFileWD: os.RelPath = os.RelPath(".gitlab/deploy-token")
  def jobTokenEnv: String = "CI_JOB_TOKEN"

  // Default token search order. Implementation picks first found and does not look for the rest.
  def tokenSearchOrder: Seq[GitlabToken] = Seq(
    Personal(Env(personalTokenEnv)),
    Personal(Property(personalTokenProperty)),
    Personal(File(personalTokenFile)),
    Personal(WorkspaceFile(personalTokenFileWD)),
    Deploy(Env(deployTokenEnv)),
    Deploy(Property(deployTokenProperty)),
    Deploy(File(deployTokenFile)),
    Deploy(WorkspaceFile(deployTokenFileWD)),
    CIJob(Env(jobTokenEnv))
  )

  // Finds gitlab token from this environment. Overriding this is not generally necessary.
  def resolveGitlabToken(
      env: Map[String, String],
      prop: Map[String, String],
      workspace: os.Path
  ): Either[String, GitlabAuthHeaders] = {

    val token = LazyList
      .from(tokenSearchOrder)
      .map(token => buildHeaders(token, env, prop, workspace))
      .find(_.isRight)
      .flatMap(_.toOption)

    token match {
      case None =>
        Left(s"Unable to find token from $tokenSearchOrder")
      case Some(headers) => Right(headers)
    }
  }

  // Converts GitlabToken to GitlabAuthHeaders. Overriding this is not generally necessary.
  def buildHeaders(
      token: GitlabToken,
      env: Map[String, String],
      prop: Map[String, String],
      workspace: os.Path
  ): Either[String, GitlabAuthHeaders] = {

    def readPath(path: os.Path): Either[String, String] =
      Try(os.read(path)).map(_.trim).toEither.left.map(e => s"failed to read file $e")

    def readSource(source: TokenSource): Either[String, String] =
      source match {
        case Env(name) =>
          env.get(name).toRight(s"Could not read environment variable $name")
        case Property(property) =>
          prop.get(property).toRight(s"Could not read system property variable $prop")
        case File(path) =>
          readPath(path)
        case WorkspaceFile(path) =>
          readPath(path.resolveFrom(workspace))
        case Custom(f) => f()
      }

    token match {
      case Personal(source) => readSource(source).map(GitlabAuthHeaders.privateToken)
      case Deploy(source) => readSource(source).map(GitlabAuthHeaders.deployToken)
      case CIJob(source) => readSource(source).map(GitlabAuthHeaders.jobToken)
      case CustomHeader(header, source) => readSource(source).map(GitlabAuthHeaders(header, _))
    }
  }

}

object GitlabTokenLookup {

  /**
   * Possible types of a Gitlab authentication header.
   *   - Personal = "Private-Token" ->
   *   - Deploy = "Deploy-Token"->
   *   - CIJob = "Job-Token" ->
   *   - CustomHeader = Use with TokenSource/Custom to produce anything you like
   *
   * Currently only one custom header is supported. If you need multiple override gitlabToken from GitlabPublishModule
   * directly
   */
  trait GitlabToken {
    def source: TokenSource
  }
  case class Personal(source: TokenSource) extends GitlabToken
  case class Deploy(source: TokenSource) extends GitlabToken
  case class CIJob(source: TokenSource) extends GitlabToken
  case class CustomHeader(header: String, source: TokenSource) extends GitlabToken

  /**
   * Possible source of token value. Either an
   *   - Env = Environment variable
   *   - Property = Javas system property
   *   - File =Contents of a file on local disk.
   *   - Custom = Own function
   *
   * Possible additions, that can now be supported with Custom: KeyVault, Yaml, etc..
   */
  sealed trait TokenSource
  case class Env(name: String) extends TokenSource
  case class File(path: os.Path) extends TokenSource
  case class WorkspaceFile(path: os.RelPath) extends TokenSource
  case class Property(property: String) extends TokenSource
  case class Custom(f: () => Either[String, String]) extends TokenSource
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy