diff --git a/README.md b/README.md index eb89aaf9..aaec9757 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Examples converted to Hummingbird 2.0 - [hello](https://github.com/hummingbird-project/hummingbird-examples/tree/main/hello) - Basic application setup +- [session](https://github.com/hummingbird-project/hummingbird-examples/tree/main/session) - Username/password and session authentication. Examples still working with Hummingbird 1.0 - [auth-cognito](https://github.com/hummingbird-project/hummingbird-examples/tree/main/auth-cognito) - Authentication via AWS Cognito. @@ -15,9 +16,6 @@ Examples still working with Hummingbird 1.0 - [multipart-form](https://github.com/hummingbird-project/hummingbird-examples/tree/main/multipart-form) - HTML form using Multipart form data, using MultipartKit - [proxy-server](https://github.com/hummingbird-project/hummingbird-examples/tree/main/proxy-server) - Using AsyncHTTPClient to build a proxy server - [proxy-server-core](https://github.com/hummingbird-project/hummingbird-examples/tree/main/proxy-server-core) - Version of proxy server only using HummingbirdCore -- [session](https://github.com/hummingbird-project/hummingbird-examples/tree/main/session) - Username/password and session authentication. -- [session-async](https://github.com/hummingbird-project/hummingbird-examples/tree/main/session-async) - Version of session sample using new swift concurrency. -- [session-dep-injection](https://github.com/hummingbird-project/hummingbird-examples/tree/main/session-dep-injection) - Version of session sample using dependency injection instead of accessing everything via `HBRequest`. - [todos-dynamodb](https://github.com/hummingbird-project/hummingbird-examples/tree/main/todos-dynamodb) - Todos application, based off [TodoBackend](http://todobackend.com) spec, using DynamoDB - [todos-dynamodb-async](https://github.com/hummingbird-project/hummingbird-examples/tree/main/todos-dynamodb-async) - Version of todos-dynamodb application using new swift concurrency. - [todos-fluent](https://github.com/hummingbird-project/hummingbird-examples/tree/main/todos-fluent) - Todos application, based off [TodoBackend](http://todobackend.com) spec, using Fluent diff --git a/session-async/.dockerignore b/session-async/.dockerignore deleted file mode 100644 index 2fb33437..00000000 --- a/session-async/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.build -.git \ No newline at end of file diff --git a/session-async/Package.swift b/session-async/Package.swift deleted file mode 100644 index 83398806..00000000 --- a/session-async/Package.swift +++ /dev/null @@ -1,53 +0,0 @@ -// swift-tools-version:5.5 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "sessions", - platforms: [.macOS("12.0")], - products: [ - .executable(name: "Server", targets: ["Server"]), - ], - dependencies: [ - .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-auth.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-fluent.git", from: "1.0.0"), - .package(url: "https://github.com/vapor/fluent-kit.git", from: "1.16.0"), - .package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.0.0"), - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), - ], - targets: [ - .executableTarget( - name: "Server", - dependencies: [ - .byName(name: "App"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - ] - ), - .target( - name: "App", - dependencies: [ - .product(name: "FluentKit", package: "fluent-kit"), - .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"), - .product(name: "Hummingbird", package: "hummingbird"), - .product(name: "HummingbirdAuth", package: "hummingbird-auth"), - .product(name: "HummingbirdFluent", package: "hummingbird-fluent"), - .product(name: "HummingbirdFoundation", package: "hummingbird"), - ], - swiftSettings: [ - // Enable better optimizations when building in Release configuration. Despite the use of - // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release - // builds. See for details. - .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)), - ] - ), - .testTarget( - name: "AppTests", - dependencies: [ - .byName(name: "App"), - .product(name: "HummingbirdXCT", package: "hummingbird"), - ] - ), - ] -) diff --git a/session-async/README.md b/session-async/README.md deleted file mode 100644 index d915a3e9..00000000 --- a/session-async/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Sessions with Persist using Async/Await - -Demonstrating a simple user/session authentication setup. Using the async/await APIs of Hummingbird. The application has three routes. -- PUT /user: Creates a new user. Requires JSON input {"name": , "password: } -- POST /user/login: Uses Basic authentication to login user and create a session -- GET /user: Returns current user - -The application uses a SQLite database accessed via Fluent to store the user table, and the `HBApplication.persist` framework. It has two `HBAsyncAuthenticator` middlewares. One that does the basic username/password authentication against the entries in the user database table and one that verifies the users session Cookie against the entries stored in persist. - -The first time you run it you should also run it with the `--migrate` command line parameter to setup the database. - -This example comes with a [PAW](https://paw.cloud/) file you can use to test the various endpoints. diff --git a/session-async/Sources/App/Application+configure.swift b/session-async/Sources/App/Application+configure.swift deleted file mode 100644 index 817a165d..00000000 --- a/session-async/Sources/App/Application+configure.swift +++ /dev/null @@ -1,36 +0,0 @@ -import FluentSQLiteDriver -import Hummingbird -import HummingbirdFluent -import HummingbirdFoundation - -extension HBApplication { - /// configure your application - /// add middleware - /// setup the encoder/decoder - /// add your routes - public func configure() throws { - // add JSON encoder/decoder as we are reading and writing JSON - self.encoder = JSONEncoder() - self.decoder = JSONDecoder() - - // add Fluent - self.addFluent() - // add sqlite database - self.fluent.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite) - // add session management using fluent - self.addSessions(using: .fluent) - // add migrations - self.fluent.migrations.add(CreateUser()) - - // add logging middleware - self.middleware.add(HBLogRequestsMiddleware(.debug)) - - // routes - self.router.get("/") { _ in - return "Hello" - } - - let userController = UserController() - userController.addRoutes(to: self.router.group("user")) - } -} diff --git a/session-async/Sources/App/Controllers/UserController.swift b/session-async/Sources/App/Controllers/UserController.swift deleted file mode 100644 index 9fdf323f..00000000 --- a/session-async/Sources/App/Controllers/UserController.swift +++ /dev/null @@ -1,62 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import NIO - -struct UserController { - /// Add routes for user controller - func addRoutes(to group: HBRouterGroup) { - group.put(use: self.create) - group.group("login").add(middleware: BasicAuthenticator()) - .post(options: .editResponse, use: self.login) - group.add(middleware: SessionAuthenticator()) - .get(use: self.current) - } - - /// Create new user - func create(_ request: HBRequest) async throws -> UserResponse { - guard let createUser = try? request.decode(as: CreateUserRequest.self) else { throw HBHTTPError(.badRequest) } - // check if user exists and if they don't then add new user - let existingUser = try await User.query(on: request.db) - .filter(\.$name == createUser.name) - .first() - // if user already exist throw conflict - guard existingUser == nil else { throw HBHTTPError(.conflict) } - - let user = User(from: createUser) - try await user.save(on: request.db) - - return UserResponse(from: user) - } - - /// Login user and create session - func login(_ request: HBRequest) async throws -> HTTPResponseStatus { - // get authenticated user and return - guard let user = request.authGet(User.self), - let userId = user.id else { throw HBHTTPError(.unauthorized) } - // create session lasting 1 hour - try await request.session.save(session: userId, expiresIn: .seconds(60)) - return .ok - } - - /// Get current logged in user - func current(_ request: HBRequest) throws -> UserResponse { - // get authenticated user and return - guard let user = request.authGet(User.self) else { throw HBHTTPError(.unauthorized) } - return UserResponse(from: user) - } -} diff --git a/session-async/Sources/App/Middleware/BasicAuthenticator.swift b/session-async/Sources/App/Middleware/BasicAuthenticator.swift deleted file mode 100644 index f2d9b51d..00000000 --- a/session-async/Sources/App/Middleware/BasicAuthenticator.swift +++ /dev/null @@ -1,33 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Hummingbird -import HummingbirdAuth - -struct BasicAuthenticator: HBAsyncAuthenticator { - func authenticate(request: HBRequest) async throws -> User? { - // does request have basic authentication info in the "Authorization" header - guard let basic = request.authBasic else { return nil } - - // check if user exists in the database and then verify the entered password - // against the one stored in the database. If it is correct then login in user - let user = try await User.query(on: request.db) - .filter(\.$name == basic.username) - .first() - guard let user = user else { return nil } - guard Bcrypt.verify(basic.password, hash: user.passwordHash) else { return nil } - return user - } -} diff --git a/session-async/Sources/App/Middleware/SessionAuthenticator.swift b/session-async/Sources/App/Middleware/SessionAuthenticator.swift deleted file mode 100644 index e96b4296..00000000 --- a/session-async/Sources/App/Middleware/SessionAuthenticator.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import HummingbirdAuth - -struct SessionAuthenticator: HBAsyncSessionAuthenticator { - typealias Session = UUID - typealias Value = User - - func getValue(from: UUID, request: Hummingbird.HBRequest) async throws -> User? { - // find user from userId - return try await User.find(from, on: request.db) - } -} diff --git a/session-async/Sources/App/Models/User.swift b/session-async/Sources/App/Models/User.swift deleted file mode 100644 index 3bdc4df3..00000000 --- a/session-async/Sources/App/Models/User.swift +++ /dev/null @@ -1,73 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import HummingbirdAuth - -/// Database description of a user -final class User: Model, HBAuthenticatable { - static let schema = "user" - - @ID(key: .id) - var id: UUID? - - @Field(key: "name") - var name: String - - @Field(key: "password-hash") - var passwordHash: String - - internal init() {} - - internal init(id: UUID? = nil, name: String, passwordHash: String) { - self.id = id - self.name = name - self.passwordHash = passwordHash - } - - internal init(from userRequest: CreateUserRequest) { - self.id = nil - self.name = userRequest.name - self.passwordHash = Bcrypt.hash(userRequest.password, cost: 12) - } -} - -/// Create user request object decoded from HTTP body -struct CreateUserRequest: Decodable { - let name: String - let password: String - - internal init(name: String, password: String) { - self.name = name - self.password = password - } -} - -/// User encoded into HTTP response -struct UserResponse: HBResponseCodable { - let id: UUID? - let name: String - - internal init(id: UUID?, name: String) { - self.id = id - self.name = name - } - - internal init(from user: User) { - self.id = user.id - self.name = user.name - } -} diff --git a/session-async/Sources/Server/main.swift b/session-async/Sources/Server/main.swift deleted file mode 100644 index d0ac5d40..00000000 --- a/session-async/Sources/Server/main.swift +++ /dev/null @@ -1,31 +0,0 @@ -import App -import ArgumentParser -import Hummingbird - -struct HummingbirdArguments: ParsableCommand { - @Option(name: .shortAndLong) - var hostname: String = "127.0.0.1" - - @Option(name: .shortAndLong) - var port: Int = 8080 - - @Flag(name: .shortAndLong) - var migrate: Bool = false - - func run() throws { - let app = HBApplication( - configuration: .init( - address: .hostname(self.hostname, port: self.port), - serverName: "Hummingbird" - ) - ) - try app.configure() - if self.migrate { - try app.fluent.migrate().wait() - } - try app.start() - app.wait() - } -} - -HummingbirdArguments.main() diff --git a/session-async/Tests/AppTests/AppTests.swift b/session-async/Tests/AppTests/AppTests.swift deleted file mode 100644 index 50ad8851..00000000 --- a/session-async/Tests/AppTests/AppTests.swift +++ /dev/null @@ -1,18 +0,0 @@ -@testable import App -import Hummingbird -import HummingbirdXCT -import XCTest - -final class AppTests: XCTestCase { - func testApp() throws { - let app = HBApplication(testing: .live) - try app.configure() - - try app.XCTStart() - defer { app.XCTStop() } - - /* try app.XCTExecute(uri: "/health", method: .GET) { response in - XCTAssertEqual(response.status, .ok) - } */ - } -} diff --git a/session-async/session-persist-async-test.paw b/session-async/session-persist-async-test.paw deleted file mode 100644 index bf1810a3..00000000 Binary files a/session-async/session-persist-async-test.paw and /dev/null differ diff --git a/session-dep-injection/Dockerfile b/session-dep-injection/Dockerfile deleted file mode 100644 index 1db54949..00000000 --- a/session-dep-injection/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -# ================================ -# Build image -# ================================ -FROM swift:5.3-focal as build - -# Install OS updates -RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ - && apt-get -q update \ - && apt-get -q dist-upgrade -y \ - && rm -rf /var/lib/apt/lists/* - -# Set up a build area -WORKDIR /build - -# First just resolve dependencies. -# This creates a cached layer that can be reused -# as long as your Package.swift/Package.resolved -# files do not change. -COPY ./Package.* ./ -RUN swift package resolve - -# Copy entire repo into container -COPY . . - -# Build everything, with optimizations and test discovery -RUN swift build --enable-test-discovery -c release - -# Switch to the staging area -WORKDIR /staging - -# Copy main executable to staging area -RUN cp "$(swift build --package-path /build -c release --show-bin-path)/Server" ./ - -# Copy any resouces from the public directory and views directory if the directories exist -# Ensure that by default, neither the directory nor any of its contents are writable. -RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true - -# ================================ -# Run image -# ================================ -FROM swift:5.3-focal-slim - -# Make sure all system packages are up to date. -RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true && \ - apt-get -q update && apt-get -q dist-upgrade -y && rm -r /var/lib/apt/lists/* - -# Create a hummingbird user and group with /app as its home directory -RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app hummingbird - -# Switch to the new home directory -WORKDIR /app - -# Copy built executable and any staged resources from builder -COPY --from=build --chown=hummingbird:hummingbird /staging /app - -# Ensure all further commands run as the vapor user -USER hummingbird:hummingbird - -# Let Docker bind to port 8080 -EXPOSE 8080 - -# Start the Vapor service when the image is run, default to listening on 8080 in production environment -ENTRYPOINT ["./Server"] -CMD ["--hostname", "0.0.0.0", "--port", "8080"] diff --git a/session-dep-injection/Sources/App/Migrations/CreateUser.swift b/session-dep-injection/Sources/App/Migrations/CreateUser.swift deleted file mode 100644 index de780e31..00000000 --- a/session-dep-injection/Sources/App/Migrations/CreateUser.swift +++ /dev/null @@ -1,29 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit - -struct CreateUser: Migration { - func prepare(on database: Database) -> EventLoopFuture { - return database.schema("user") - .id() - .field("name", .string, .required) - .field("password-hash", .string, .required) - .create() - } - - func revert(on database: Database) -> EventLoopFuture { - return database.schema("user").delete() - } -} diff --git a/session/.dockerignore b/session/.dockerignore deleted file mode 100644 index 2fb33437..00000000 --- a/session/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.build -.git \ No newline at end of file diff --git a/session/Dockerfile b/session/Dockerfile deleted file mode 100644 index 1db54949..00000000 --- a/session/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -# ================================ -# Build image -# ================================ -FROM swift:5.3-focal as build - -# Install OS updates -RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ - && apt-get -q update \ - && apt-get -q dist-upgrade -y \ - && rm -rf /var/lib/apt/lists/* - -# Set up a build area -WORKDIR /build - -# First just resolve dependencies. -# This creates a cached layer that can be reused -# as long as your Package.swift/Package.resolved -# files do not change. -COPY ./Package.* ./ -RUN swift package resolve - -# Copy entire repo into container -COPY . . - -# Build everything, with optimizations and test discovery -RUN swift build --enable-test-discovery -c release - -# Switch to the staging area -WORKDIR /staging - -# Copy main executable to staging area -RUN cp "$(swift build --package-path /build -c release --show-bin-path)/Server" ./ - -# Copy any resouces from the public directory and views directory if the directories exist -# Ensure that by default, neither the directory nor any of its contents are writable. -RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true - -# ================================ -# Run image -# ================================ -FROM swift:5.3-focal-slim - -# Make sure all system packages are up to date. -RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true && \ - apt-get -q update && apt-get -q dist-upgrade -y && rm -r /var/lib/apt/lists/* - -# Create a hummingbird user and group with /app as its home directory -RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app hummingbird - -# Switch to the new home directory -WORKDIR /app - -# Copy built executable and any staged resources from builder -COPY --from=build --chown=hummingbird:hummingbird /staging /app - -# Ensure all further commands run as the vapor user -USER hummingbird:hummingbird - -# Let Docker bind to port 8080 -EXPOSE 8080 - -# Start the Vapor service when the image is run, default to listening on 8080 in production environment -ENTRYPOINT ["./Server"] -CMD ["--hostname", "0.0.0.0", "--port", "8080"] diff --git a/session/Package.swift b/session/Package.swift deleted file mode 100644 index 079f53ad..00000000 --- a/session/Package.swift +++ /dev/null @@ -1,53 +0,0 @@ -// swift-tools-version:5.5 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "sessions", - platforms: [.macOS(.v10_15)], - products: [ - .executable(name: "Server", targets: ["Server"]), - ], - dependencies: [ - .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-auth.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-fluent.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-redis.git", from: "1.0.0"), - .package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.0.0"), - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), - ], - targets: [ - .executableTarget( - name: "Server", - dependencies: [ - .byName(name: "App"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - ] - ), - .target( - name: "App", - dependencies: [ - .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"), - .product(name: "Hummingbird", package: "hummingbird"), - .product(name: "HummingbirdAuth", package: "hummingbird-auth"), - .product(name: "HummingbirdFluent", package: "hummingbird-fluent"), - .product(name: "HummingbirdFoundation", package: "hummingbird"), - .product(name: "HummingbirdRedis", package: "hummingbird-redis"), - ], - swiftSettings: [ - // Enable better optimizations when building in Release configuration. Despite the use of - // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release - // builds. See for details. - .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)), - ] - ), - .testTarget( - name: "AppTests", - dependencies: [ - .byName(name: "App"), - .product(name: "HummingbirdXCT", package: "hummingbird"), - ] - ), - ] -) diff --git a/session/README.md b/session/README.md deleted file mode 100644 index 0c10a908..00000000 --- a/session/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Sessions with Redis - -Demonstrating a simple user/session authentication setup. The application has three routes. -- PUT /user: Creates a new user. Requires JSON input {"name": , "password: } -- POST /user/login: Uses Basic authentication to login user and create a session -- GET /user: Returns current user - -The application uses a SQLite database accessed via Fluent to store the user table, and a Redis instance attached to the `HBApplication.persist` framework. It has two `HBAuthenticator` middlewares. One that does the basic username/password authentication against the entries in the user database table and one that verifies the users session Cookie against the entries stored in persist. - -For this example to work you need a running Redis instance. -``` -docker run -p 6379:6379 redis -``` -The first time you run it you should also run it with the `--migrate` command line parameter to setup the database. - -This example comes with a [PAW](https://paw.cloud/) file you can use to test the various endpoints. diff --git a/session/Sources/App/Application+configure.swift b/session/Sources/App/Application+configure.swift deleted file mode 100644 index 6974ee5f..00000000 --- a/session/Sources/App/Application+configure.swift +++ /dev/null @@ -1,39 +0,0 @@ -import FluentSQLiteDriver -import Hummingbird -import HummingbirdFluent -import HummingbirdFoundation -import HummingbirdRedis - -extension HBApplication { - /// configure your application - /// add middleware - /// setup the encoder/decoder - /// add your routes - public func configure() throws { - // add JSON encoder/decoder as we are reading and writing JSON - self.encoder = JSONEncoder() - self.decoder = JSONDecoder() - - // add Fluent - self.addFluent() - // add sqlite database - self.fluent.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite) - // add migrations - self.fluent.migrations.add(CreateUser()) - // add redis - try self.addRedis(configuration: .init(hostname: "localhost", port: 6379)) - // add session management - self.addSessions(using: .redis) - - // add logging middleware - self.middleware.add(HBLogRequestsMiddleware(.debug)) - - // routes - self.router.get("/") { _ in - return "Hello" - } - - let userController = UserController() - userController.addRoutes(to: self.router.group("user")) - } -} diff --git a/session/Sources/App/Controllers/UserController.swift b/session/Sources/App/Controllers/UserController.swift deleted file mode 100644 index ffb44762..00000000 --- a/session/Sources/App/Controllers/UserController.swift +++ /dev/null @@ -1,65 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import NIO - -struct UserController { - /// Add routes for user controller - func addRoutes(to group: HBRouterGroup) { - group.put(use: self.create) - group.group("login").add(middleware: BasicAuthenticator()) - .post(options: .editResponse, use: self.login) - group.add(middleware: SessionAuthenticator()) - .get(use: self.current) - } - - /// Create new user - func create(_ request: HBRequest) -> EventLoopFuture { - guard let createUser = try? request.decode(as: CreateUserRequest.self) else { return request.failure(.badRequest) } - let user = User(from: createUser) - // check if user exists and if they don't then add new user - return User.query(on: request.db) - .filter(\.$name == user.name) - .first() - .flatMapThrowing { user -> Void in - // if user already exist throw conflict - guard user == nil else { throw HBHTTPError(.conflict) } - return - } - .flatMap { _ in - return user.save(on: request.db) - } - .transform(to: UserResponse(from: user)) - } - - /// Login user and create session - func login(_ request: HBRequest) -> EventLoopFuture { - // get authenticated user and return - guard let user = request.authGet(User.self), - let userId = user.id else { return request.failure(.unauthorized) } - // create session lasting 1 hour - return request.session.save(session: userId, expiresIn: .seconds(60)) - .map { .ok } - } - - /// Get current logged in user - func current(_ request: HBRequest) throws -> UserResponse { - // get authenticated user and return - guard let user = request.authGet(User.self) else { throw HBHTTPError(.unauthorized) } - return UserResponse(from: user) - } -} diff --git a/session/Sources/App/Middleware/BasicAuthenticator.swift b/session/Sources/App/Middleware/BasicAuthenticator.swift deleted file mode 100644 index 29c709db..00000000 --- a/session/Sources/App/Middleware/BasicAuthenticator.swift +++ /dev/null @@ -1,39 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Hummingbird -import HummingbirdAuth - -struct BasicAuthenticator: HBAuthenticator { - func authenticate(request: HBRequest) -> EventLoopFuture { - // does request have basic authentication info in the "Authorization" header - guard let basic = request.authBasic else { return request.success(nil) } - - // check if user exists in the database and then verify the entered password - // against the one stored in the database. If it is correct then login in user - return User.query(on: request.db) - .filter(\.$name == basic.username) - .first() - .map { user -> User? in - guard let user = user else { return nil } - if Bcrypt.verify(basic.password, hash: user.passwordHash) { - return user - } - return nil - } - // hop back to request eventloop - .hop(to: request.eventLoop) - } -} diff --git a/session/Sources/App/Middleware/SessionAuthenticator.swift b/session/Sources/App/Middleware/SessionAuthenticator.swift deleted file mode 100644 index b193ae18..00000000 --- a/session/Sources/App/Middleware/SessionAuthenticator.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import HummingbirdAuth - -struct SessionAuthenticator: HBSessionAuthenticator { - typealias Session = UUID - typealias Value = User - - func getValue(from: UUID, request: Hummingbird.HBRequest) -> NIOCore.EventLoopFuture { - // find user from userId - return User.find(from, on: request.db) - } -} diff --git a/session/Sources/App/Migrations/CreateUser.swift b/session/Sources/App/Migrations/CreateUser.swift deleted file mode 100644 index de780e31..00000000 --- a/session/Sources/App/Migrations/CreateUser.swift +++ /dev/null @@ -1,29 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit - -struct CreateUser: Migration { - func prepare(on database: Database) -> EventLoopFuture { - return database.schema("user") - .id() - .field("name", .string, .required) - .field("password-hash", .string, .required) - .create() - } - - func revert(on database: Database) -> EventLoopFuture { - return database.schema("user").delete() - } -} diff --git a/session/Sources/App/Models/User.swift b/session/Sources/App/Models/User.swift deleted file mode 100644 index 3bdc4df3..00000000 --- a/session/Sources/App/Models/User.swift +++ /dev/null @@ -1,73 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Hummingbird server framework project -// -// Copyright (c) 2021-2021 the Hummingbird authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import FluentKit -import Foundation -import Hummingbird -import HummingbirdAuth - -/// Database description of a user -final class User: Model, HBAuthenticatable { - static let schema = "user" - - @ID(key: .id) - var id: UUID? - - @Field(key: "name") - var name: String - - @Field(key: "password-hash") - var passwordHash: String - - internal init() {} - - internal init(id: UUID? = nil, name: String, passwordHash: String) { - self.id = id - self.name = name - self.passwordHash = passwordHash - } - - internal init(from userRequest: CreateUserRequest) { - self.id = nil - self.name = userRequest.name - self.passwordHash = Bcrypt.hash(userRequest.password, cost: 12) - } -} - -/// Create user request object decoded from HTTP body -struct CreateUserRequest: Decodable { - let name: String - let password: String - - internal init(name: String, password: String) { - self.name = name - self.password = password - } -} - -/// User encoded into HTTP response -struct UserResponse: HBResponseCodable { - let id: UUID? - let name: String - - internal init(id: UUID?, name: String) { - self.id = id - self.name = name - } - - internal init(from user: User) { - self.id = user.id - self.name = user.name - } -} diff --git a/session/Sources/Server/main.swift b/session/Sources/Server/main.swift deleted file mode 100644 index d0ac5d40..00000000 --- a/session/Sources/Server/main.swift +++ /dev/null @@ -1,31 +0,0 @@ -import App -import ArgumentParser -import Hummingbird - -struct HummingbirdArguments: ParsableCommand { - @Option(name: .shortAndLong) - var hostname: String = "127.0.0.1" - - @Option(name: .shortAndLong) - var port: Int = 8080 - - @Flag(name: .shortAndLong) - var migrate: Bool = false - - func run() throws { - let app = HBApplication( - configuration: .init( - address: .hostname(self.hostname, port: self.port), - serverName: "Hummingbird" - ) - ) - try app.configure() - if self.migrate { - try app.fluent.migrate().wait() - } - try app.start() - app.wait() - } -} - -HummingbirdArguments.main() diff --git a/session/Tests/AppTests/AppTests.swift b/session/Tests/AppTests/AppTests.swift deleted file mode 100644 index 50ad8851..00000000 --- a/session/Tests/AppTests/AppTests.swift +++ /dev/null @@ -1,18 +0,0 @@ -@testable import App -import Hummingbird -import HummingbirdXCT -import XCTest - -final class AppTests: XCTestCase { - func testApp() throws { - let app = HBApplication(testing: .live) - try app.configure() - - try app.XCTStart() - defer { app.XCTStop() } - - /* try app.XCTExecute(uri: "/health", method: .GET) { response in - XCTAssertEqual(response.status, .ok) - } */ - } -} diff --git a/session/session-persist-test.paw b/session/session-persist-test.paw deleted file mode 100644 index 81a828dc..00000000 Binary files a/session/session-persist-test.paw and /dev/null differ diff --git a/session-async/Dockerfile b/sessions/Dockerfile similarity index 100% rename from session-async/Dockerfile rename to sessions/Dockerfile diff --git a/session-dep-injection/Package.swift b/sessions/Package.swift similarity index 100% rename from session-dep-injection/Package.swift rename to sessions/Package.swift diff --git a/session-dep-injection/README.md b/sessions/README.md similarity index 100% rename from session-dep-injection/README.md rename to sessions/README.md diff --git a/session-dep-injection/Sources/App/Application+build.swift b/sessions/Sources/App/Application+build.swift similarity index 100% rename from session-dep-injection/Sources/App/Application+build.swift rename to sessions/Sources/App/Application+build.swift diff --git a/session-dep-injection/Sources/App/Controllers/UserController.swift b/sessions/Sources/App/Controllers/UserController.swift similarity index 100% rename from session-dep-injection/Sources/App/Controllers/UserController.swift rename to sessions/Sources/App/Controllers/UserController.swift diff --git a/session-dep-injection/Sources/App/Middleware/BasicAuthenticator.swift b/sessions/Sources/App/Middleware/BasicAuthenticator.swift similarity index 100% rename from session-dep-injection/Sources/App/Middleware/BasicAuthenticator.swift rename to sessions/Sources/App/Middleware/BasicAuthenticator.swift diff --git a/session-dep-injection/Sources/App/Middleware/SessionAuthenticator.swift b/sessions/Sources/App/Middleware/SessionAuthenticator.swift similarity index 100% rename from session-dep-injection/Sources/App/Middleware/SessionAuthenticator.swift rename to sessions/Sources/App/Middleware/SessionAuthenticator.swift diff --git a/session-async/Sources/App/Migrations/CreateUser.swift b/sessions/Sources/App/Migrations/CreateUser.swift similarity index 100% rename from session-async/Sources/App/Migrations/CreateUser.swift rename to sessions/Sources/App/Migrations/CreateUser.swift diff --git a/session-dep-injection/Sources/App/Models/User.swift b/sessions/Sources/App/Models/User.swift similarity index 100% rename from session-dep-injection/Sources/App/Models/User.swift rename to sessions/Sources/App/Models/User.swift diff --git a/session-dep-injection/Sources/App/app.swift b/sessions/Sources/App/app.swift similarity index 100% rename from session-dep-injection/Sources/App/app.swift rename to sessions/Sources/App/app.swift diff --git a/session-dep-injection/Tests/AppTests/AppTests.swift b/sessions/Tests/AppTests/AppTests.swift similarity index 100% rename from session-dep-injection/Tests/AppTests/AppTests.swift rename to sessions/Tests/AppTests/AppTests.swift diff --git a/session-dep-injection/session-persist-async-test.paw b/sessions/session-persist-async-test.paw similarity index 100% rename from session-dep-injection/session-persist-async-test.paw rename to sessions/session-persist-async-test.paw