OCI: support Basic authentication scheme (#145)

* OCI: support Basic authentication scheme

* .isValid → .isValid()

* tart login: make --username optional
This commit is contained in:
Nikolay Edigaryev 2022-07-08 16:36:00 +03:00 committed by GitHub
parent 4386192161
commit 52abb7589c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 15 deletions

View File

@ -9,13 +9,13 @@ struct Login: AsyncParsableCommand {
var host: String
@Option(help: "username")
var username: String
var username: String?
@Flag(help: "password-stdin")
var passwordStdin: Bool = false
func validate() throws {
let usernameProvided = !username.isEmpty
let usernameProvided = username != nil
let passwordProvided = passwordStdin
if usernameProvided != passwordProvided {
@ -28,7 +28,7 @@ struct Login: AsyncParsableCommand {
var user: String
var password: String
if !username.isEmpty {
if let username = username {
user = username
password = readLine()!
} else {

View File

@ -0,0 +1,21 @@
import Foundation
protocol Authentication {
func header() -> (String, String)
func isValid() -> Bool
}
struct BasicAuthentication: Authentication {
let user: String
let password: String
func header() -> (String, String) {
let creds = Data("\(user):\(password)".utf8).base64EncodedString()
return ("Authorization", "Basic \(creds)")
}
func isValid() -> Bool {
true
}
}

View File

@ -25,7 +25,7 @@ extension HTTPClientResponse.Body {
}
}
struct TokenResponse: Decodable {
struct TokenResponse: Decodable, Authentication {
let defaultIssuedAt = Date()
let defaultExpiresIn = 60
@ -65,10 +65,12 @@ struct TokenResponse: Decodable {
}
}
var isValid: Bool {
get {
Date() < tokenExpiresAt
}
func header() -> (String, String) {
("Authorization", "Bearer \(token)")
}
func isValid() -> Bool {
Date() < tokenExpiresAt
}
}
@ -83,7 +85,7 @@ class Registry {
let namespace: String
let credentialsProvider: CredentialsProvider
var currentAuthToken: TokenResponse? = nil
var currentAuthToken: Authentication? = nil
init(urlComponents: URLComponents,
namespace: String,
@ -245,7 +247,7 @@ class Registry {
}
// Invalidate token if it has expired
if currentAuthToken?.isValid == false {
if currentAuthToken?.isValid() == false {
currentAuthToken = nil
}
@ -266,6 +268,15 @@ class Registry {
}
let wwwAuthenticate = try WWWAuthenticate(rawHeaderValue: wwwAuthenticateRaw)
if wwwAuthenticate.scheme == "Basic" {
if let (user, password) = try credentialsProvider.retrieve(host: baseURL.host!) {
currentAuthToken = BasicAuthentication(user: user, password: password)
}
return
}
if wwwAuthenticate.scheme != "Bearer" {
throw RegistryError.AuthFailed(why: "WWW-Authenticate header's authentication scheme "
+ "\"\(wwwAuthenticate.scheme)\" is unsupported, expected \"Bearer\" scheme")
@ -316,7 +327,8 @@ class Registry {
var request = request
if let token = currentAuthToken {
request.headers.add(name: "Authorization", value: "Bearer \(token.token)")
let (name, value) = token.header()
request.headers.add(name: name, value: value)
}
return try await httpClient.execute(request, deadline: .distantFuture)

View File

@ -11,7 +11,7 @@ final class TokenResponseTests: XCTestCase {
let expectedTokenExpiresAtRange = Date()...Date().addingTimeInterval(60)
XCTAssertTrue(expectedTokenExpiresAtRange.contains(tokenResponse.tokenExpiresAt))
XCTAssertTrue(tokenResponse.isValid)
XCTAssertTrue(tokenResponse.isValid())
}
func testExpirationBasic() throws {
@ -23,9 +23,9 @@ final class TokenResponseTests: XCTestCase {
let expectedTokenExpiresAtRange = Date()...Date().addingTimeInterval(2)
XCTAssertTrue(expectedTokenExpiresAtRange.contains(tokenResponse.tokenExpiresAt))
XCTAssertTrue(tokenResponse.isValid)
XCTAssertTrue(tokenResponse.isValid())
_ = XCTWaiter.wait(for: [expectation(description: "Wait 3 seconds for the token to become invalid")], timeout: 2)
XCTAssertFalse(tokenResponse.isValid)
XCTAssertFalse(tokenResponse.isValid())
}
func testExpirationWithIssuedAt() throws {
@ -33,6 +33,6 @@ final class TokenResponseTests: XCTestCase {
let tokenResponse = try TokenResponse.parse(fromData: tokenResponseRaw)
XCTAssertEqual(Date(timeIntervalSince1970: 3600), tokenResponse.tokenExpiresAt)
XCTAssertFalse(tokenResponse.isValid)
XCTAssertFalse(tokenResponse.isValid())
}
}