Let's Go Further Ch 14-18
Run in Background Helper
A nice little helper function to run things in the background without all the boilerplate.
func (app *application) background(fn func()) {
// Launch a background goroutine.
go func() {
// Recover any panic.
defer func() {
if err := recover(); err != nil {
app.logger.PrintError(fmt.Errorf("%s", err), nil)
}
}()
// Execute the arbitrary function that we passed as the parameter.
fn()
}()
}
Sending a activation token to a new user
General pattern
- User Signs Up
- User receives an email with a “activate your account” link
- User clicks on link to activate account
The endpoint created to activate a new user account MUST NOT be a GET
endpoint.
Why?
Some browsers will request URLs in the background. So if you open an email, there is a chance the browser will request the URL and in turn will activate your new user account. This is a problem when there is a malicious actor who creates a new user account with your email account, you open the email because you were curious and inadvertantly give access to the malicious actor.
Make sure that any actions which change the state of your application (including activating a user) are only ever executed via POST, PUT, PATCH or DELETE requests — not by GET requests
In production, you should also constrict activation tokens to only be accepted over an encrypted HTTPS connection, not via regular HTTP.
Setting Up User Permissions
This was a lot more simple than I thought it would be.
The general pattern is:
- Set up a Permissions and a Users_Permissions table
- Create a Model to handle DB transactions
- Create a middleware to check & require user permissions
- Attach middleware to certain routes that require user permissions
Example:
CREATE TABLE IF NOT EXISTS permissions (
id bigserial PRIMARY KEY,
code text NOT NULL
);
CREATE TABLE IF NOT EXISTS users_permissions (
user_id bigint NOT NULL REFERENCES users ON DELETE CASCADE,
permission_id bigint NOT NULL REFERENCES permissions ON DELETE CASCADE,
PRIMARY KEY (user_id, permission_id)
);
-- Add the two permissions to the table.
INSERT INTO permissions (code)
VALUES
('movies:read'),
('movies:write');
One thing that I would change from the example in the book. Instead of using a string “movies:read” everytime you want to reference a specific permission. I would create a constant variable for it. That way the code base would be less error prone due to typos.
The example also does not cover users with higher permissions. I assume you would just need to create a specific endpoint for granting permissions.