manageable-users/README.md

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.