module Bootstrap open System type App = { Name: string User: string Org: string Scopes: string list Client: string } type Session = { User: string; State: obj } type Store = { Read: string -> Map Write: string -> Map -> unit } type Quay = { LoginAuth: unit -> string Login: string -> string -> string -> Session Access: string -> App -> unit Application: string -> App -> string Assign: string -> Session -> App -> unit Issue: Session -> App -> string } type Config = { AdminPath: string } let apps = [ { Name = "candidates-reader" User = "candidates-bot" Org = "candidates" Scopes = [ "repo:read" ] Client = "" } { Name = "candidates-writer" User = "candidates-bot" Org = "candidates" Scopes = [ "repo:create"; "repo:write" ] Client = "" } { Name = "mirror-reader" User = "mirror-bot" Org = "mirror" Scopes = [ "repo:read" ] Client = "" } { Name = "mirror-writer" User = "mirror-bot" Org = "mirror" Scopes = [ "repo:create"; "repo:write" ] Client = "" } ] let personaPath user = $"secret/data/quay/personas/{user}" let tokenPath app = $"secret/data/quay/oauth/{app.Name}" let private field name (values: Map) = match Map.tryFind name values with | Some value when not (String.IsNullOrWhiteSpace value) && value <> "PLACEHOLDER" -> value | _ -> invalidOp "credential is absent" let run config store quay = let pending = apps |> List.filter (fun app -> let values = store.Read(tokenPath app) match Map.tryFind "token" values with | Some value when not (String.IsNullOrWhiteSpace value) && value <> "PLACEHOLDER" -> false | _ -> true) if not pending.IsEmpty then let admin = store.Read config.AdminPath |> field "token" let loginAuth = quay.LoginAuth() let sessions = pending |> List.map _.User |> List.distinct |> List.map (fun user -> let password = store.Read(personaPath user) |> field "password" let session = quay.Login user password loginAuth let app = pending |> List.find (fun app -> app.User = user) quay.Access admin app user, session) |> Map.ofList for definition in pending do let client = quay.Application admin definition let app = { definition with Client = client } let session = sessions[app.User] quay.Assign admin session app let token = quay.Issue session app store.Write (tokenPath app) (Map.ofList (List.singleton ("token", token)))