diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ab1063..cc0b0801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Updated stac-fastapi libraries to v3.0.0a1 [#265](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/265) - Updated stac-fastapi libraries to v3.0.0a3 [#269](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/269) +- Converted Basic auth to a route dependency and merged with new route depndencies method. [#251](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/251) +- Updated docker-compose.basic_auth_protected.yml to use STAC_FASTAPI_ROUTE_DEPENDENCIES environment variable. [#251](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/251) ### Fixed diff --git a/README.md b/README.md index f50a4047..2e56c452 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@

- [![PyPI version](https://badge.fury.io/py/stac-fastapi.elasticsearch.svg)](https://badge.fury.io/py/stac-fastapi.elasticsearch) + [![PyPI version](https://badge.fury.io/py/stac-fastapi.elasticsearch.svg)](https://badge.fury.io/py/stac-fastapi.elasticsearch) [![Join the chat at https://gitter.im/stac-fastapi-elasticsearch/community](https://badges.gitter.im/stac-fastapi-elasticsearch/community.svg)](https://gitter.im/stac-fastapi-elasticsearch/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - + --- @@ -22,15 +22,15 @@ --- -### Notes: +### Notes: -- Our Api core library can be used to create custom backends. See [stac-fastapi-mongo](https://github.com/Healy-Hyperspatial/stac-fastapi-mongo) for a working example. +- Our Api core library can be used to create custom backends. See [stac-fastapi-mongo](https://github.com/Healy-Hyperspatial/stac-fastapi-mongo) for a working example. - Reach out on our [Gitter](https://app.gitter.im/#/room/#stac-fastapi-elasticsearch_community:gitter.im) channel or feel free to add to our [Discussions](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions) page here on github. - There is [Postman](https://documenter.getpostman.com/view/12888943/2s8ZDSdRHA) documentation here for examples on how to run some of the API routes locally - after starting the elasticsearch backend via the docker-compose.yml file. -- The `/examples` folder shows an example of running stac-fastapi-elasticsearch from PyPI in docker without needing any code from the repository. There is also a Postman collection here that you can load into Postman for testing the API routes. - -- For changes, see the [Changelog](CHANGELOG.md) -- We are always welcoming contributions. For the development notes: [Contributing](CONTRIBUTING.md) +- The `/examples` folder shows an example of running stac-fastapi-elasticsearch from PyPI in docker without needing any code from the repository. There is also a Postman collection here that you can load into Postman for testing the API routes. + +- For changes, see the [Changelog](CHANGELOG.md) +- We are always welcoming contributions. For the development notes: [Contributing](CONTRIBUTING.md) ### To install from PyPI: @@ -38,25 +38,25 @@ ```shell pip install stac_fastapi.elasticsearch ``` -or +or ``` pip install stac_fastapi.opensearch ``` - + ## Build Elasticsearch API backend ```shell docker-compose up elasticsearch docker-compose build app-elasticsearch ``` - + ## Running Elasticsearch API on localhost:8080 ```shell docker-compose up app-elasticsearch ``` -By default, docker-compose uses Elasticsearch 8.x and OpenSearch 2.11.1. +By default, docker-compose uses Elasticsearch 8.x and OpenSearch 2.11.1. If you wish to use a different version, put the following in a file named `.env` in the same directory you run docker-compose from: @@ -76,7 +76,7 @@ curl -X "POST" "http://localhost:8080/collections" \ }' ``` -Note: this "Collections Transaction" behavior is not part of the STAC API, but may be soon. +Note: this "Collections Transaction" behavior is not part of the STAC API, but may be soon. ## Configure the API @@ -91,12 +91,12 @@ The application root path is left as the base url by default. If deploying to AW The collections route handles optional `limit` and `token` parameters. The `links` field that is returned from the `/collections` route contains a `next` link with the token that can be used to get the next page of results. - + ```shell curl -X "GET" "http://localhost:8080/collections?limit=1&token=example_token" ``` -## Ingesting Sample Data CLI Tool +## Ingesting Sample Data CLI Tool ```shell Usage: data_loader.py [OPTIONS] @@ -114,12 +114,12 @@ Options: ```shell python3 data_loader.py --base-url http://localhost:8080 -``` +``` ## Elasticsearch Mappings -Mappings apply to search index, not source. The mappings are stored in index templates on application startup. +Mappings apply to search index, not source. The mappings are stored in index templates on application startup. These templates will be used implicitly when creating new Collection and Item indices. @@ -275,112 +275,6 @@ curl -X "POST" "http://localhost:9200/_aliases" \ The modified Items with lowercase identifiers will now be visible to users accessing `my-collection` in the STAC API. -## Basic Auth - -#### Environment Variable Configuration - -Basic authentication is an optional feature that can be enabled through [Route Dependencies](#route-dependencies). - - -### Example Configuration - -This example illustrates the configuration for two users: an **admin** user with full permissions (*) and a **reader** user with limited permissions to specific read-only endpoints. -```json -[ - { - "routes": [ - { - "method": "*", - "path": "*" - } - ], - "dependencies": [ - { - "method": "stac_fastapi.core.basic_auth.BasicAuth", - "kwargs": { - "credentials":[ - { - "username": "admin", - "password": "admin" - } - ] - } - } - ] - }, - { - "routes": [ - {"path": "/", "method": ["GET"]}, - {"path": "/conformance", "method": ["GET"]}, - {"path": "/collections/{collection_id}/items/{item_id}", "method": ["GET"]}, - {"path": "/search", "method": ["GET", "POST"]}, - {"path": "/collections", "method": ["GET"]}, - {"path": "/collections/{collection_id}", "method": ["GET"]}, - {"path": "/collections/{collection_id}/items", "method": ["GET"]}, - {"path": "/queryables", "method": ["GET"]}, - {"path": "/queryables/collections/{collection_id}/queryables", "method": ["GET"]}, - {"path": "/_mgmt/ping", "method": ["GET"]} - ], - "dependencies": [ - { - "method": "stac_fastapi.core.basic_auth.BasicAuth", - "kwargs": { - "credentials":[ - { - "username": "reader", - "password": "reader" - } - ] - } - } - ] - } -] -``` - -## Route Dependencies - -### Configuration - -Route dependencies for endpoints can enable through the `STAC_FASTAPI_ROUTE_DEPENDENCIES` -environment variable as a path to a JSON file or a JSON string. - -#### Route Dependency - -A Route Dependency must include `routes`, a list of at least one [Route](#routes), and `dependencies` a -list of at least one [Dependency](#dependencies). - -#### Routes - -A Route must include a `path`, the relative path to the endpoint, and a `method`, the request method of the path. - -#### Dependencies - -A Dependency must include the `method`, a dot seperated path to the Class or Function, and -can include any `args` or `kwargs` for the method. - -#### Example -``` -STAC_FASTAPI_ROUTE_DEPENDENCIES=[ - { - "routes": [ - { - "method": "GET", - "path": "/collections" - } - ], - "dependencies": [ - { - "method": "fastapi.security.OAuth2PasswordBearer", - "kwargs": { - "tokenUrl": "token" - } - } - ] - } -] -``` - -### Docker Compose Configurations +## Auth -See `docker-compose.basic_auth_protected.yml` and `docker-compose.basic_auth_public.yml` for basic authentication configurations. \ No newline at end of file +Authentication is an optional feature that can be enabled through `Route Dependencies` examples can be found and a more detailed explanation in [examples/auth](examples/auth). diff --git a/examples/auth/README.md b/examples/auth/README.md new file mode 100644 index 00000000..bd74c439 --- /dev/null +++ b/examples/auth/README.md @@ -0,0 +1,217 @@ +# Authentication + +Authentication is an optional feature that can be enabled through [Route Dependencies](#route-dependencies). + +## Route Dependencies + +### Configuration + +Route dependencies for endpoints can enable through the `STAC_FASTAPI_ROUTE_DEPENDENCIES` +environment variable as a path to a JSON file or a JSON string. + +#### Route Dependency + +A Route Dependency must include `routes`, a list of at least one [Route](#routes), and `dependencies` a +list of at least one [Dependency](#dependencies). + +#### Routes + +A Route must include a `path`, the relative path to the endpoint, and a `method`, the request method of the path. + +#### Dependencies + +A Dependency must include the `method`, a dot seperated path to the [Dependency](https://fastapi.tiangolo.com/tutorial/dependencies), and +can include any `args` or `kwargs` for the method. + +#### Example +``` +STAC_FASTAPI_ROUTE_DEPENDENCIES=[ + { + "routes": [ + { + "method": "GET", + "path": "/collections" + } + ], + "dependencies": [ + { + "method": "fastapi.security.OAuth2PasswordBearer", + "kwargs": { + "tokenUrl": "token" + } + } + ] + } +] +``` + +## Examples + +[docker-compose.route_dependencies.yml](docker-compose.route_dependencies.yml), [docker-compose.basic_auth.yml](docker-compose.basic_auth.yml), and [docker-compose.oauth2.yml](docker-compose.oauth2.yml) + give example for 3 different authentication configurations. + +### Route dependencies + +[docker-compose.route_dependencies.yml](docker-compose.route_dependencies.yml) gives an example of +the `STAC_FASTAPI_ROUTE_DEPENDENCIES` environment variable adding the `conftest.must_be_bob` route +dependency to the `GET` method on `/collections` endpoint. + +#### Configuration + +```json +[ + { + "routes": [ + { + "method": "GET", + "path": "/collections" + } + ], + "dependencies": [ + { + "method": "conftest.must_be_bob" + } + ] + } +] +``` + +### Basic Auth + +This example illustrates how to add the [Basic Auth](../../stac_fastapi/core/stac_fastapi/core/basic_auth.py) Route Denpendency +which allows a list of `user` and `password` pairs to be used to protect the specified routes. +The example defines two users: an **admin** user with full permissions (*) and a **reader** user with +limited permissions to specific read-only endpoints. + +#### Configuration + +```json +[ + { + "routes": [ + { + "method": "*", + "path": "*" + } + ], + "dependencies": [ + { + "method": "stac_fastapi.core.basic_auth.BasicAuth", + "kwargs": { + "credentials":[ + { + "username": "admin", + "password": "admin" + } + ] + } + } + ] + }, + { + "routes": [ + {"path": "/", "method": ["GET"]}, + {"path": "/conformance", "method": ["GET"]}, + {"path": "/collections/{collection_id}/items/{item_id}", "method": ["GET"]}, + {"path": "/search", "method": ["GET", "POST"]}, + {"path": "/collections", "method": ["GET"]}, + {"path": "/collections/{collection_id}", "method": ["GET"]}, + {"path": "/collections/{collection_id}/items", "method": ["GET"]}, + {"path": "/queryables", "method": ["GET"]}, + {"path": "/queryables/collections/{collection_id}/queryables", "method": ["GET"]}, + {"path": "/_mgmt/ping", "method": ["GET"]} + ], + "dependencies": [ + { + "method": "stac_fastapi.core.basic_auth.BasicAuth", + "kwargs": { + "credentials":[ + { + "username": "reader", + "password": "reader" + } + ] + } + } + ] + } +] +``` + +### Oauth2 + +This example illustrates how the `STAC_FASTAPI_ROUTE_DEPENDENCIES` environment variable can be used to point to a JSON file. + +The [FastAPI OAuth2PasswordBearer](../../stac_fastapi/core/stac_fastapi/core/basic_auth.py) Denpendency is applied to all routes +and methods using the `*` wildcard. This dependeny follows the [Oauth 2.0 Password Grant](https://oauth.net/2/grant-types/password) flow. + +The [Basic Auth](../../stac_fastapi/core/stac_fastapi/core/basic_auth.py) Denpendency is also applied to the `GET` method +on `/collections` endpoint. To demonstate how multiple dependencies can be applied to one endpoint. + + +#### Keycloak + +For the Oauth 2.0 flow [Keycloak](https://www.keycloak.org/) has been used as the identity provider, as it supports [OIDC](https://www.microsoft.com/en-us/security/business/security-101/what-is-openid-connect-oidc) (an extension to OAuth2). + +In the Password Grant flow the user authenticates with the Keycloak server and recieves an authorization token. This token +is then used by STAC FastAPI to verify (via the Keycloak server) the user's identity and permissions. [This article](https://darutk.medium.com/diagrams-of-all-the-openid-connect-flows-6968e3990660) +gives a nice visual explanation of many of the OpenID connet flows. + +The Keycloak server is prepopulated with a `STAC` realm with one user `bob` with the password `bobpass` as an example. [This article](https://wkrzywiec.medium.com/create-and-configure-keycloak-oauth-2-0-authorization-server-f75e2f6f6046) +gives the steps to set up a Keycloak server with Docker. And [this guide](https://www.keycloak.org/server/importExport) shows how to import +and export realms. + +#### Configuration + +```json +[ + { + "routes": [ + { + "method": "*", + "path": "*" + } + ], + "dependencies": [ + { + "method": "fastapi.security.OAuth2PasswordBearer", + "kwargs": { + "tokenUrl": "http://Keycloak:8083/auth/realms/stac/protocol/openid-connect/token" + } + } + ] + }, + { + "routes": [ + { + "path": "/collections/{collection_id}/items/{item_id}", + "method": "GET" + }, + { + "path": "/search", + "method": [ + "GET", + "POST" + ] + }, + { + "path": "/collections", + "method": "GET" + } + ], + "dependencies": [ + { + "method": "stac_fastapi.core.basic_auth.BasicAuth", + "kwargs": { + "credentials": [ + { + "username": "reader", + "password": "reader" + } + ] + } + } + ] + } +] +``` diff --git a/docker-compose.basic_auth_protected.yml b/examples/auth/docker-compose.basic_auth.yml similarity index 87% rename from docker-compose.basic_auth_protected.yml rename to examples/auth/docker-compose.basic_auth.yml index 012a9953..a6292a1f 100644 --- a/docker-compose.basic_auth_protected.yml +++ b/examples/auth/docker-compose.basic_auth.yml @@ -26,9 +26,9 @@ services: ports: - "8080:8080" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./scripts:/app/scripts - - ./esdata:/usr/share/elasticsearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ../../scripts:/app/scripts + - ../../esdata:/usr/share/elasticsearch/data depends_on: - elasticsearch command: @@ -59,9 +59,9 @@ services: ports: - "8082:8082" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./scripts:/app/scripts - - ./osdata:/usr/share/opensearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ../../scripts:/app/scripts + - ../../osdata:/usr/share/opensearch/data depends_on: - opensearch command: @@ -74,8 +74,8 @@ services: environment: ES_JAVA_OPTS: -Xms512m -Xmx1g volumes: - - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - - ./elasticsearch/snapshots:/usr/share/elasticsearch/snapshots + - ../../elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml + - ../../elasticsearch/snapshots:/usr/share/elasticsearch/snapshots ports: - "9200:9200" @@ -88,7 +88,7 @@ services: - plugins.security.disabled=true - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m volumes: - - ./opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml - - ./opensearch/snapshots:/usr/share/opensearch/snapshots + - ../../opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml + - ../../opensearch/snapshots:/usr/share/opensearch/snapshots ports: - "9202:9202" diff --git a/docker-compose.route_dependencies_file.yml b/examples/auth/docker-compose.oauth2.yml similarity index 82% rename from docker-compose.route_dependencies_file.yml rename to examples/auth/docker-compose.oauth2.yml index 3ac11b16..8cd8f72f 100644 --- a/docker-compose.route_dependencies_file.yml +++ b/examples/auth/docker-compose.oauth2.yml @@ -26,10 +26,10 @@ services: ports: - "8080:8080" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./examples/route_dependencies:/app/route_dependencies - - ./scripts:/app/scripts - - ./esdata:/usr/share/elasticsearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ./route_dependencies:/app/route_dependencies + - ../../scripts:/app/scripts + - ../../esdata:/usr/share/elasticsearch/data depends_on: - elasticsearch command: @@ -60,9 +60,9 @@ services: ports: - "8082:8082" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./scripts:/app/scripts - - ./osdata:/usr/share/opensearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ../../scripts:/app/scripts + - ../../osdata:/usr/share/opensearch/data depends_on: - opensearch command: @@ -75,8 +75,8 @@ services: environment: ES_JAVA_OPTS: -Xms512m -Xmx1g volumes: - - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - - ./elasticsearch/snapshots:/usr/share/elasticsearch/snapshots + - ../../elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml + - ../../elasticsearch/snapshots:/usr/share/elasticsearch/snapshots ports: - "9200:9200" @@ -89,8 +89,8 @@ services: - plugins.security.disabled=true - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m volumes: - - ./opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml - - ./opensearch/snapshots:/usr/share/opensearch/snapshots + - ../../opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml + - ../../opensearch/snapshots:/usr/share/opensearch/snapshots ports: - "9202:9202" @@ -120,7 +120,7 @@ services: - KC_DB_USERNAME=keycloak - KC_DB_PASSWORD=password volumes: - - ./example/route_dependencies/stac-realm.json:/opt/keycloak/data/import + - ./keycloak/stac-realm.json:/opt/keycloak/data/import command: start-dev --import-realm depends_on: - postgres diff --git a/docker-compose.route_dependencies_env.yml b/examples/auth/docker-compose.route_dependencies.yml similarity index 81% rename from docker-compose.route_dependencies_env.yml rename to examples/auth/docker-compose.route_dependencies.yml index 9adf19b1..b10fbb6f 100644 --- a/docker-compose.route_dependencies_env.yml +++ b/examples/auth/docker-compose.route_dependencies.yml @@ -26,9 +26,9 @@ services: ports: - "8080:8080" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./scripts:/app/scripts - - ./esdata:/usr/share/elasticsearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ../../scripts:/app/scripts + - ../../esdata:/usr/share/elasticsearch/data depends_on: - elasticsearch command: @@ -59,9 +59,9 @@ services: ports: - "8082:8082" volumes: - - ./stac_fastapi:/app/stac_fastapi - - ./scripts:/app/scripts - - ./osdata:/usr/share/opensearch/data + - ../../stac_fastapi:/app/stac_fastapi + - ../../scripts:/app/scripts + - ../../osdata:/usr/share/opensearch/data depends_on: - opensearch command: @@ -74,8 +74,8 @@ services: environment: ES_JAVA_OPTS: -Xms512m -Xmx1g volumes: - - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - - ./elasticsearch/snapshots:/usr/share/elasticsearch/snapshots + - ../../elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml + - ../../elasticsearch/snapshots:/usr/share/elasticsearch/snapshots ports: - "9200:9200" @@ -88,7 +88,7 @@ services: - plugins.security.disabled=true - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m volumes: - - ./opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml - - ./opensearch/snapshots:/usr/share/opensearch/snapshots + - ../../opensearch/config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml + - ../../opensearch/snapshots:/usr/share/opensearch/snapshots ports: - "9202:9202" diff --git a/examples/route_dependencies/stac-realm.json b/examples/auth/keycloak/stac-realm.json similarity index 100% rename from examples/route_dependencies/stac-realm.json rename to examples/auth/keycloak/stac-realm.json diff --git a/examples/route_dependencies/route_dependencies.json b/examples/auth/route_dependencies/route_dependencies.json similarity index 100% rename from examples/route_dependencies/route_dependencies.json rename to examples/auth/route_dependencies/route_dependencies.json