Skip to content

Commit

Permalink
Modernize and overhaul everything, in short (#3)
Browse files Browse the repository at this point in the history
* Rewrite the entire OAuth and request handling logic. Modernize the package.

* Require the fixed Vapor version. Strip OneRoster suffices from OAuth 2 provider URLs (but not the request base URL!).

* Implement more of the multi-object request test. Not complete.

* Add bare minimum trivial test of the multi-item request.

* Add the OAuth2 URL handling to the Request version

* Add additional logging. Decode User.userIds optionally due to non-conforming implementations.
  • Loading branch information
gwynne authored Nov 3, 2021
1 parent a865ac4 commit 64f24f1
Show file tree
Hide file tree
Showing 15 changed files with 961 additions and 584 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/.build
/Packages
/*.xcodeproj
.swiftpm
.swiftpm
Package.resolved
151 changes: 0 additions & 151 deletions Package.resolved

This file was deleted.

9 changes: 5 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// swift-tools-version:5.2
// swift-tools-version:5.5
import PackageDescription

let package = Package(
name: "OneRoster",
platforms: [
.macOS(.v10_15)
.macOS(.v12)
],
products: [
.library(name: "OneRoster", targets: ["OneRoster"]),
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.52.1"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
],
targets: [
Expand All @@ -19,7 +19,8 @@ let package = Package(
.product(name: "Logging", package: "swift-log"),
]),
.testTarget(name: "OneRosterTests", dependencies: [
.target(name: "OneRoster")
.target(name: "OneRoster"),
.product(name: "XCTVapor", package: "vapor"),
])
]
)
60 changes: 60 additions & 0 deletions Sources/OneRoster/Client/Application+OneRosterClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Application+OneRosterClient.swift
// OneRoster
//
// Created by Jimmy McDermott on 4/10/20.
//

import Foundation
import Vapor

extension Application {
/// Get a `OneRosterClient` suitable for making OneRoster requests to the given base URL without authentication.
///
/// Uses the application's default `Client` and `Logger`.
///
/// - Important: The base URL is allowed to be either a true "base" (the root to which the OneRoster RESTful path
/// and version should be appended, i.e. <https://example.com/oneroster>) or the RESTful base (e.g.
/// <https://example.com/oneroster/ims/oneroster/v1p1>). The difference is detected based on the presence or
/// absence of the `/ims/oneroster/v1p1` path components at the end of the provided URL. If that suffix is
/// missing, it will be added for all OneRoster requests. **However**, if the suffix _is_ provided, it is stripped
/// for requests relating to authorization, such as OAuth 2 token grant requests.
public func oneRoster(baseUrl: URL) -> OneRosterClient {
return OneRosterClient(baseUrl: baseUrl, client: self.client, logger: self.logger)
}

/// Get a `OneRosterClient` suitable for making OneRoster requests to the given base URL using OAuth1 authentication
/// with the given credentials.
///
/// Indirectly uses the application's default `Client` and `Logger`.
public func oauth1OneRoster(baseUrl: URL, clientId: String, clientSecret: String) -> OneRosterClient {
return OneRosterClient(baseUrl: baseUrl, client: self.oauth1(parameters: .init(clientId: clientId, clientSecret: clientSecret)), logger: self.logger)
}

/// Get a `OneRosterClient` suitable for making OneRoster requests to the given base URL using OAuth2 authentication
/// with the given credentials and scope (defaults to the "core read-only" scope).
///
/// Indirectly uses the application's default `Client` and `Logger`.
///
/// - Important: A base URL is used to construct the OAuth 2 endpoint for making token grant requests, _regardless_
/// of whether or not it has the OneRoster RESTful API path suffix. See `oneRoster(baseUrl:)` for more details.
public func oauth2OneRoster(
baseUrl: URL, clientId: String, clientSecret: String,
scope: String = "https://purl.imsglobal.org/spec/or/v1p1/scope/roster-core.readonly"
) -> OneRosterClient {
var providerUrl = baseUrl

if providerUrl.pathComponents.suffix(3) == ["ims", "oneroster", "v1p1"] {
providerUrl.deleteLastPathComponent()
providerUrl.deleteLastPathComponent()
providerUrl.deleteLastPathComponent()
}
return OneRosterClient(
baseUrl: baseUrl,
client: self.oauth2(parameters: { $0.eventLoop.makeSucceededFuture(.init(
providerBaseUrl: providerUrl, clientId: clientId, clientSecret: clientSecret, scopes: scope
)) }),
logger: self.logger
)
}
}
Loading

0 comments on commit 64f24f1

Please sign in to comment.