Skip to content

Commit

Permalink
DESENG-492 Enhance analytics API (#2397)
Browse files Browse the repository at this point in the history
* Enhance analytics api
  • Loading branch information
VineetBala-AOT authored Feb 28, 2024
1 parent 05be8f3 commit 28fd55e
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 106 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/analytics-api-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ jobs:
KEYCLOAK_TEST_BASE_URL: "http://localhost:8081"
KEYCLOAK_TEST_REALMNAME: "demo"
USE_TEST_KEYCLOAK_DOCKER: "YES"

SQLALCHEMY_DATABASE_URI: "postgresql://postgres:postgres@localhost:5432/postgres"

runs-on: ubuntu-20.04

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## February 27, 2024
- **Task**Enhance analytics api for Improved Readability and Maintainability [DESENG-492](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-492)
- Refactor analytics-api config to harmonize its structure and conventions with met-api.
- Ensure the sample.env file maintains consistent formatting.
- Adjusted the component_id column size in the comment table of the met-api to resolve an error encountered during user submissions.

## February 26, 2024
- **Task**Models for dynamic engagement pages [DESENG-500](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-500)
- Implemented endpoints for dynamic engagement pages, including summary and custom sections.
Expand Down
6 changes: 3 additions & 3 deletions analytics-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ Flask-Migrate==2.7.0
Flask-Moment==1.0.5
Flask-SQLAlchemy==2.5.1
Flask-Script==2.0.5
Flask==2.2.3
Flask==2.2.5
Jinja2==3.0.3
Mako==1.2.4
MarkupSafe==2.1.2
SQLAlchemy-Utils==0.40.0
SQLAlchemy==1.3.24
SQLAlchemy==1.4.17
Werkzeug==2.2.3
alembic==1.10.3
aniso8601==9.0.1
Expand All @@ -23,7 +23,7 @@ charset-normalizer==3.1.0
click==8.1.3
ecdsa==0.18.0
flask-jwt-oidc==0.3.0
flask-marshmallow==0.11.0
flask-marshmallow==0.14.0
flask-restx==1.1.0
gunicorn==20.1.0
idna==3.4
Expand Down
60 changes: 44 additions & 16 deletions analytics-api/sample.env
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
# GDX MET Analytics API Configuration
# For more information on these values, please see the documentation
# or analytics-api/src/analytics-api/config.py

# Changes Flask's run mode and the set of env vars are used to configure the app. You should not need to change this here.
FLASK_ENV=development

# local db variables
DATABASE_USERNAME=analytics
DATABASE_PASSWORD=analytics
DATABASE_NAME=met
DATABASE_HOST=localhost
DATABASE_PORT=5432

JWT_OIDC_WELL_KNOWN_CONFIG=https://localhost:8080/auth/realms/met/.well-known/openid-configuration
JWT_OIDC_AUDIENCE=account
JWT_OIDC_ISSUER=https://localhost:8080/auth/realms/met
JWT_OIDC_ALGORITHMS=RS256
JWT_OIDC_JWKS_URI=https://localhost:8080/auth/realms/met/protocol/openid-connect/certs
JWT_OIDC_CACHING_ENABLED=True
JWT_OIDC_JWKS_CACHE_TIMEOUT=3000000

CORS_ORIGIN=http://localhost:3000,http://localhost:5000
USE_DEBUG=True # Enable a dev-friendly debug mode
TESTING= # Handle errors normally (False) or raise exceptions (True)

# CORS Settings
CORS_ORIGINS=http://localhost:3000,http://localhost:5000

# Miscellaneous Settings
SECRET_KEY="" # For Flask sessions. If unset, this value is randomized

# Database Configuration
DATABASE_HOST="localhost"
DATABASE_PORT="5432"
DATABASE_USERNAME="postgres"
DATABASE_PASSWORD="postgres"
DATABASE_NAME="met"
#Default: set from above settings (this overrides them)
SQLALCHEMY_ECHO=
SQLALCHEMY_TRACK_MODIFICATIONS=

# Keycloak configuration.
KEYCLOAK_BASE_URL="" # auth-server-url
KEYCLOAK_REALMNAME="" # realm

# JWT OIDC configuration for authentication
JWT_OIDC_AUDIENCE="" # resource
JWT_OIDC_ISSUER="" # default: constructed from base url and realm name
JWT_OIDC_WELL_KNOWN_CONFIG="" # default: constructed from issuer
JWT_OIDC_JWKS_URI="" # default: constructed from issuer
JWT_OIDC_ROLE_CLAIM=client_roles # Keycloak schema
JWT_OIDC_CACHING_ENABLED=true # Enable caching of JWKS.
JWT_OIDC_JWKS_CACHE_TIMEOUT=300 # Timeout for JWKS cache in seconds.

# Test database settings
# If unset, uses the same settings as the main database
DATABASE_TEST_USERNAME=
DATABASE_TEST_PASSWORD=
DATABASE_TEST_NAME=
DATABASE_TEST_HOST=
DATABASE_TEST_PORT=
19 changes: 16 additions & 3 deletions analytics-api/src/analytics_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'development')):
# All configuration are in config file
app.config.from_object(get_named_config(run_mode))

CORS(app, supports_credentials=True)
CORS(app, origins=app.config['CORS_ORIGINS'], supports_credentials=True)

# Register blueprints
app.register_blueprint(API_BLUEPRINT)
Expand Down Expand Up @@ -75,8 +75,21 @@ def set_secure_headers(response):
def setup_jwt_manager(app_context, jwt_manager):
"""Use flask app to configure the JWTManager to work for a particular Realm."""

def get_roles(a_dict):
return a_dict['realm_access']['roles'] # pragma: no cover
def get_roles(token_info) -> list:
"""
Consumes a token_info dictionary and returns a list of roles.
Uses a configurable path to the roles in the token_info dictionary.
"""
role_access_path = app_context.config['JWT_CONFIG']['ROLE_CLAIM']
for key in role_access_path.split('.'):
token_info = token_info.get(key, None)
if token_info is None:
app_context.logger.warning('Unable to find role in token_info. '
'Please check your JWT_ROLE_CALLBACK '
'configuration.')
return []
return token_info

app_context.config['JWT_ROLE_CALLBACK'] = get_roles
jwt_manager.init_app(app_context)
16 changes: 10 additions & 6 deletions analytics-api/src/analytics_api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@
from flask_jwt_oidc import JwtManager
from flask_jwt_oidc.exceptions import AuthError

jwt = (
JwtManager()
) # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps
auth_methods = { # for swagger documentation
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
}


class Auth: # pylint: disable=too-few-public-methods
class Auth(JwtManager): # pylint: disable=too-few-public-methods
"""Extending JwtManager to include additional functionalities."""

@classmethod
def require(cls, f):
"""Validate the Bearer Token."""

@jwt.requires_auth
@auth.requires_auth
@wraps(f)
def decorated(*args, **kwargs):
g.authorization_header = request.headers.get('Authorization', None)
Expand Down Expand Up @@ -59,6 +63,6 @@ def decorated(*args, **kwargs):
return decorated


auth = (
jwt = auth = (
Auth()
)
Loading

0 comments on commit 28fd55e

Please sign in to comment.