156 lines
6.2 KiB
Markdown
156 lines
6.2 KiB
Markdown
# ManageableUsers
|
|
|
|
A package providing basic user management when using the Vapor web framework and a PostgreSQL database.
|
|
|
|
Provides controllers and middleware for authenticating users. This includes web pages for logging in,
|
|
handling of forgotten password by sending tokens via email, and logging out.
|
|
|
|
Provides controllers for managing users. This includes administration pages and inviting users via email.
|
|
|
|
*Note: This package uses raw SQL for accessing the database, so there are no `Fluent` model for users,
|
|
but the database is connected using `Fluent`, so the it can still be used for other items.*
|
|
|
|
## Try out
|
|
|
|
The package contains an executable target `SampleApp` with a minimal implementation demonstrating the
|
|
functionality.
|
|
|
|
You can try this to asses the functionality provided by this package, or you can use it as a template for starting your own project.
|
|
|
|
### Database
|
|
|
|
To run `SampleApp`, a database is needed.
|
|
|
|
If you don't already have PostgreSQL installed, refer to [postgresql.org](https://www.postgresql.org/docs/current/tutorial-install.html)
|
|
for options.
|
|
|
|
There's a SQL script provided for creating the tables needed by `SampleApp`.
|
|
```
|
|
$ psql --file=Resources/Database/Create.sql
|
|
```
|
|
|
|
For creating a new database named `sampleapp`, create the tables, and a corresponding user do the following:
|
|
```
|
|
$ psql --dbname=postgres --command="CREATE DATABASE sampleapp"
|
|
$ psql --dbname=sampleapp --command="CREATE ROLE sampleapp WITH LOGIN PASSWORD 'sampleapp_password'"
|
|
$ psql --dbname=sampleapp --file=Resources/Database/Create.sql
|
|
$ psql --dbname=sampleapp --command="GRANT CONNECT ON DATABASE sampleapp TO sampleapp"
|
|
$ psql --dbname=sampleapp --command="GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO sampleapp"
|
|
```
|
|
|
|
There's also a script for creating an initial user:
|
|
```
|
|
$ psql --dbname=sampleapp --file=Resources/Database/InitialUser.sql --variable=email=someone@example.com
|
|
```
|
|
|
|
The script will return a password URL for setting the password (assuming that the service runs on https://example.com/, it needs to be changed to the actual host to be usable).
|
|
The email address is used for logging in, and can also be used for resetting passwords via email if you have email delivery setup.
|
|
|
|
### Running Application
|
|
|
|
You can run the application with `swift run`, or from an IDE such as Xcode.
|
|
|
|
A number of environment variables are needed for proper function.
|
|
- `DATABASE_HOST`: Host for the PostgreSQL server, defaults to `localhost`
|
|
- `DATABASE_PORT`: Port for the PostgreSQL server, defaults to `5432`
|
|
- `DATABASE_NAME`: Name of the database, defaults to `sampleapp`
|
|
- `DATABASE_USERNAME`: Username for accessing the database, defaults to `sampleapp`
|
|
- `DATABASE_PASSWORD`: Password for accessing the database, defaults to `sampleapp_password`
|
|
- `BASE_URL`: URL for accessing the web site (e.g. `http://localhost:8080`)
|
|
- `SMTP_HOST`, `SMTP_PORT`, `SMTP_ENCRYPTION`, `SMTP_TIMEOUT`, `SMTP_USERNAME`, `SMTP_PASSWORD` and `SMTP_USE_ESMTP`: Configuring email delivery, see [the documentation for the SwiftSMTP package](https://sersoft-gmbh.github.io/swift-smtp/2.15.0/documentation/swiftsmtp#Creating-a-Configuration) for details.
|
|
- `LOG_LEVEL`: Desired level of logging, defaulting to `info`.
|
|
|
|
The application will write to the log that it has started and which port it's listening on.
|
|
|
|
### Using application
|
|
|
|
Use the URL returned from the `InitialUser.sql` script above in a browser. Make sure to adapt it to the actual setup
|
|
(e.g. `http://localhost:8080/auth/password/2lG91bumxxvm9Ky7xLy1Nxz2mJoxjRCS` with defaults and without a reverse proxy).
|
|
|
|
A form for setting a password should be provided at that URL. After setting a password, that password and the email
|
|
address provided to the `InitialUser.sql` script can then be used for logging in.
|
|
|
|
## Getting Started
|
|
|
|
Create a new Vapor project, or reuse an existing one.
|
|
|
|
### Dependencies
|
|
|
|
Add dependency on `manageable-users` by adding the package to `Package.swift` as
|
|
```swift
|
|
.package(url: "https://git.carlberg.org/public/manageable-users.git", from: "1.0.0"),
|
|
```
|
|
|
|
…and adding to the executable target's dependencies as
|
|
```swift
|
|
.product(name: "ManageableUsers", package: "manageable-users"),
|
|
``
|
|
|
|
Copy the contents of the `Resources/Views` folder from the `manageable-users` into the `Resources/Views` folder
|
|
in the new project, and also copy the contents of the `Public` folder into the `Public` folder.
|
|
|
|
In the `configure.swift` file, add the import
|
|
```swift
|
|
import ManageableUsers
|
|
```
|
|
|
|
…and add
|
|
```swift
|
|
try await ManageableUsers.configure (app)
|
|
```
|
|
|
|
into the `configure()` function.
|
|
|
|
In the `routes.swift` file, add the import
|
|
```swift
|
|
import ManageableUsers
|
|
```
|
|
|
|
…and replace everything in the `routes()` function with
|
|
```swift
|
|
let sessioned = app.grouped (app.sessions.middleware)
|
|
.grouped (BasicUser.sessionAuthenticator())
|
|
let loggedIn = sessioned
|
|
.grouped (BasicUser.redirectMiddleware (path: "auth/login"))
|
|
let api = sessioned
|
|
.grouped("api")
|
|
|
|
AuthenticationController<BasicUser>().routes (sessioned, api: api)
|
|
AdminController<BasicUser>().routes (loggedIn, api: api)
|
|
|
|
struct WelcomeContext: Encodable {
|
|
let user: BasicUser?
|
|
let section: String
|
|
}
|
|
|
|
loggedIn.get { req async throws in
|
|
return try await req.view.render("welcome", WelcomeContext (user: req.auth.get (BasicUser.self), section: "home"))
|
|
}
|
|
```
|
|
|
|
## Extending
|
|
|
|
To add useful functionality, just add more routes to `routes.swift`.
|
|
|
|
To use a consistent look, use `base.leaf` in your Leaf templates for pages.
|
|
|
|
To add items to the main menu, do that in `configure.swift` by replacing
|
|
|
|
```swift
|
|
try await ManageableUsers.configure (app)
|
|
```
|
|
|
|
with something like
|
|
|
|
```swift
|
|
try await ManageableUsers.configure(app, mainMenu: [MenuItem (section: "inventory", path: "inventory", name: "Inventory")])
|
|
```
|
|
|
|
For `MenuItem`
|
|
- `section` corresponds to the `section` included in the rendering context supplied to Leaf.
|
|
- `path` corresponds to the URL path to the start page of that section.
|
|
- `name` is the name that will be displayed in the menu.
|
|
- `role` is an optional role that is required to access that section.
|
|
- If `nil` or omitted, it will be shown to all logged in users.
|
|
- Corresponds to the `name` column in the `roles` table.
|