Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle permissions #12

Open
zachflanders opened this issue Sep 1, 2024 · 1 comment
Open

Handle permissions #12

zachflanders opened this issue Sep 1, 2024 · 1 comment

Comments

@zachflanders
Copy link
Owner

Make sure that users can only access their own layers

@zachflanders
Copy link
Owner Author

Ensure that Django only serves layers to layer owners

  • Add rest framework permission classes

Ensure that tileserv only serves layers to layer owners:

pg_tileserv is designed to work with PostgreSQL and supports SQL-based security policies like Row-Level Security (RLS), as well as passing environment variables (such as JWT tokens) to Postgres through its configuration.

Here’s a summary of how you can achieve your goal using built-in mechanisms and Postgres capabilities, without modifying the pg_tileserv codebase:

1. Configure pg_tileserv to Pass the JWT Token

You can configure pg_tileserv to pass the JWT token to PostgreSQL without modifying the core code. This is done through configuration settings in pg_tileserv's config.toml file.

Here’s what you need to do:

  • Edit the pg_tileserv configuration file (config.toml, usually found in /etc/pg_tileserv/):
  • Enable JWT handling by configuring pg_tileserv to extract the JWT token from the Authorization header and pass it to PostgreSQL as a session variable:
[auth.jwt]
enabled = true
header = "Authorization"   # The header where the JWT is sent
prefix = "Bearer "         # Remove the "Bearer " prefix from the token

Pass the token as a session variable in Postgres:

With the above configuration, pg_tileserv will automatically pass the token to PostgreSQL by setting a session variable, such as jwt.claims.token. This variable will be available in Postgres queries.

2. Modify PostgreSQL for JWT Token Handling

You don’t need to modify pg_tileserv directly because the core of the access control happens in PostgreSQL using SQL and RLS. Follow these steps in your Postgres database:

Set up a function to extract the user_id from the JWT token:

The token will be passed from pg_tileserv to Postgres as a session variable. You can create a Postgres function that decodes the token and extracts the user_id:
CREATE OR REPLACE FUNCTION extract_user_id(jwt_token text)
RETURNS int AS $$
DECLARE
  user_id int;
BEGIN
  -- Extract the 'user_id' from the JWT token (assuming it's part of the claims)
  SELECT jwt_claim(jwt_token, 'user_id')::int INTO user_id;
  
  -- Ensure we have a valid user_id
  IF user_id IS NULL THEN
    RAISE EXCEPTION 'Invalid or missing user_id in JWT';
  END IF;

  RETURN user_id;
END;
$$ LANGUAGE plpgsql;

Apply Row-Level Security (RLS) in Postgres:

RLS policies ensure that only the layer's owner can access their data. You can create policies like this:

    ALTER TABLE layers ENABLE ROW LEVEL SECURITY;

    CREATE POLICY user_layer_policy
    ON layers
    FOR SELECT
    USING (user_id = extract_user_id(current_setting('jwt.claims.token')));
This policy checks the user_id in the JWT token (passed via pg_tileserv) and ensures that only the authorized user can access their layers.

3. Frontend: Sending the JWT Token

In the frontend, you can continue sending the JWT token in the Authorization header with requests to pg_tileserv. OpenLayers allows you to customize the request headers via a tileLoadFunction or similar mechanisms.

const token = localStorage.getItem('jwtToken'); // Or fetch from wherever you're storing it

const vectorTileLayer = new VectorTileLayer({
  source: new VectorTileSource({
    format: new MVT(),
    tileLoadFunction: function (tile, src) {
      const xhr = new XMLHttpRequest();
      xhr.open('GET', src);
      xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      xhr.responseType = 'arraybuffer';

      xhr.onload = function () {
        const data = xhr.response;
        const format = new MVT();
        const features = format.readFeatures(new Uint8Array(data), {
          extent: tile.getExtent(),
          projection: tile.getProjection(),
        });
        tile.setFeatures(features);
        tile.setLoaded();
      };

      xhr.onerror = function () {
        console.error('Failed to load tile');
        tile.setLoaded(); // Mark the tile as "loaded" to avoid hanging
      };

      xhr.send();
    },
  }),
});

Summary

You can achieve authentication and access control through the following steps:

- Configure pg_tileserv to pass the JWT token as a session variable in Postgres.
- Implement Row-Level Security (RLS) and use Postgres functions to extract and validate the user_id from the JWT token.
- Pass the JWT token from the frontend in the Authorization header.

This setup allows you to control access securely based on the user's JWT token, with no need to modify the pg_tileserv source code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant