Skip to content

Commit

Permalink
Merge pull request #8 from dinesh-aot/EPICSYSTEM-230
Browse files Browse the repository at this point in the history
GET /staff-users
  • Loading branch information
dinesh-aot authored Aug 8, 2024
2 parents 252a5b3 + 94498d1 commit 466a004
Show file tree
Hide file tree
Showing 27 changed files with 713 additions and 206 deletions.
4 changes: 2 additions & 2 deletions compliance-api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ services:
environment:
- POSTGRES_USER=compliance
- POSTGRES_PASSWORD=compliance
- POSTGRES_DB=compliance
- POSTGRES_DB=compliance-db
- POSTGRES_HOST_AUTH_METHOD=trust
ports:
- 54332:5432/tcp
- 44332:5432/tcp
restart: unless-stopped

compliance-api-db-test:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"""staff and position tables
Revision ID: 5c3d1411be77
Revises: 20bcb68bc2ac
Create Date: 2024-08-02 14:27:42.565023
"""
from datetime import datetime
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "5c3d1411be77"
down_revision = "20bcb68bc2ac"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
position_table = op.create_table(
"positions",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(length=100), nullable=True),
sa.Column("description", sa.String(length=500), nullable=True),
sa.Column("created_date", sa.DateTime(), nullable=False),
sa.Column("updated_date", sa.DateTime(), nullable=True),
sa.Column("created_by", sa.String(length=100), nullable=False),
sa.Column("updated_by", sa.String(length=100), nullable=True),
sa.Column("is_active", sa.Boolean(), server_default="t", nullable=False),
sa.Column("is_deleted", sa.Boolean(), server_default="f", nullable=False),
sa.PrimaryKeyConstraint("id"),
)
with op.batch_alter_table("staff_users", schema=None) as batch_op:
batch_op.add_column(sa.Column("position_id", sa.Integer(), nullable=False))
batch_op.add_column(
sa.Column("deputy_director_id", sa.Integer(), nullable=True)
)
batch_op.add_column(sa.Column("supervisor_id", sa.Integer(), nullable=True))
batch_op.add_column(
sa.Column("auth_user_guid", sa.String(length=100), nullable=True)
)
batch_op.add_column(
sa.Column("is_active", sa.Boolean(), server_default="t", nullable=False)
)
batch_op.add_column(
sa.Column("is_deleted", sa.Boolean(), server_default="f", nullable=False)
)
batch_op.alter_column(
"created_by",
existing_type=sa.VARCHAR(length=50),
type_=sa.String(length=100),
nullable=False,
)
batch_op.alter_column(
"updated_by",
existing_type=sa.VARCHAR(length=50),
type_=sa.String(length=100),
existing_nullable=True,
)
batch_op.drop_index("ix_staff_users_username")
batch_op.create_index(
batch_op.f("ix_staff_users_auth_user_guid"), ["auth_user_guid"], unique=True
)
batch_op.create_foreign_key(
"staff_users_deputy_director_id_fkey",
"staff_users",
["deputy_director_id"],
["id"],
)
batch_op.create_foreign_key(
"staff_users_position_id_fkey", "positions", ["position_id"], ["id"]
)
batch_op.create_foreign_key(
"staff_users_supervisor_id_fkey", "staff_users", ["supervisor_id"], ["id"]
)
batch_op.drop_column("contact_number")
batch_op.drop_column("username")
batch_op.drop_column("email_address")
batch_op.drop_column("middle_name")
op.bulk_insert(
position_table,
[
{
"name": "Compliance & Enforcement Officer",
"description": "Compliance and Enforcement Officer Position",
"created_date": datetime.utcnow(),
"created_by": "system", # Or replace with the actual creator
},
{
"name": "Senior Compliance & Enforcement Officer",
"description": "Senior Compliance and Enforcement Officer Position",
"created_date": datetime.utcnow(),
"created_by": "system", # Or replace with the actual creator
},
{
"name": "Deputy Director Operations",
"description": "Deputy Director Operations Position",
"created_date": datetime.utcnow(),
"created_by": "system", # Or replace with the actual creator
},
{
"name": "Director",
"description": "Director Position",
"created_date": datetime.utcnow(),
"created_by": "system", # Or replace with the actual creator
},
],
)

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("staff_users", schema=None) as batch_op:
batch_op.add_column(
sa.Column(
"middle_name", sa.VARCHAR(length=50), autoincrement=False, nullable=True
)
)
batch_op.add_column(
sa.Column(
"email_address",
sa.VARCHAR(length=100),
autoincrement=False,
nullable=True,
)
)
batch_op.add_column(
sa.Column(
"username", sa.VARCHAR(length=100), autoincrement=False, nullable=True
)
)
batch_op.add_column(
sa.Column(
"contact_number",
sa.VARCHAR(length=50),
autoincrement=False,
nullable=True,
)
)
batch_op.drop_constraint("staff_users_supervisor_id_fkey", type_="foreignkey")
batch_op.drop_constraint("staff_users_position_id_fkey", type_="foreignkey")
batch_op.drop_constraint(
"staff_users_deputy_director_id_fkey", type_="foreignkey"
)
batch_op.drop_index(batch_op.f("ix_staff_users_auth_user_guid"))
batch_op.create_index("ix_staff_users_username", ["username"], unique=True)
batch_op.alter_column(
"updated_by",
existing_type=sa.String(length=100),
type_=sa.VARCHAR(length=50),
existing_nullable=True,
)
batch_op.alter_column(
"created_by",
existing_type=sa.String(length=100),
type_=sa.VARCHAR(length=50),
nullable=True,
)
batch_op.drop_column("is_deleted")
batch_op.drop_column("is_active")
batch_op.drop_column("auth_user_guid")
batch_op.drop_column("supervisor_id")
batch_op.drop_column("deputy_director_id")
batch_op.drop_column("position_id")

op.drop_table("positions")
# ### end Alembic commands ###
Empty file modified compliance-api/pre-hook-update-db.sh
100755 → 100644
Empty file.
3 changes: 2 additions & 1 deletion compliance-api/sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ S3_ACCESS_KEY_ID=compliance-admin
S3_SECRET_ACCESS_KEY=<GENERATED_S3_SECRET>
S3_HOST=
S3_REGION=
S3_SERVICE=
S3_SERVICE=
AUTH_BASE_URL=
29 changes: 29 additions & 0 deletions compliance-api/src/compliance_api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
# limitations under the License.
"""Bring in the common JWT Manager."""
from functools import wraps
from http import HTTPStatus

from flask import g, request
from flask_jwt_oidc import JwtManager

from compliance_api.exceptions import PermissionDeniedError


jwt = (
JwtManager()
Expand All @@ -40,5 +43,31 @@ def decorated(*args, **kwargs):

return decorated

@classmethod
def has_one_of_roles(cls, roles):
"""Check that at least one of the realm roles are in the token.
Args:
roles [str,]: Comma separated list of valid roles
"""

def decorated(f):
@Auth.require
@wraps(f)
def wrapper(*args, **kwargs):
if jwt.contains_role(roles):
return f(*args, **kwargs)

raise PermissionDeniedError("Access Denied", HTTPStatus.UNAUTHORIZED)

return wrapper

return decorated

@classmethod
def has_role(cls, role):
"""Validate the role."""
return jwt.validate_roles(role)


auth = Auth()
32 changes: 15 additions & 17 deletions compliance-api/src/compliance_api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class _Config: # pylint: disable=too-few-public-methods
# TODO API client wont need user management roles in keycloak.
KEYCLOAK_ADMIN_USERNAME = os.getenv("MET_ADMIN_CLIENT_ID")
KEYCLOAK_ADMIN_SECRET = os.getenv("MET_ADMIN_CLIENT_SECRET")
AUTH_BASE_URL = os.getenv("AUTH_BASE_URL")


class DevConfig(_Config): # pylint: disable=too-few-public-methods
Expand All @@ -101,8 +102,6 @@ class DevConfig(_Config): # pylint: disable=too-few-public-methods
class TestConfig(_Config): # pylint: disable=too-few-public-methods
"""In support of testing only.used by the py.test suite."""

DEBUG = True
TESTING = True
DEBUG = True
TESTING = True

Expand All @@ -124,7 +123,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods
JWT_OIDC_WELL_KNOWN_CONFIG = os.getenv("JWT_OIDC_WELL_KNOWN_CONFIG")
JWT_OIDC_TEST_ALGORITHMS = os.getenv("JWT_OIDC_TEST_ALGORITHMS")
JWT_OIDC_TEST_JWKS_URI = os.getenv("JWT_OIDC_TEST_JWKS_URI", default=None)

JWT_OIDC_TEST_KEYS = {
'keys': [
{
Expand Down Expand Up @@ -162,20 +160,20 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods
}

JWT_OIDC_TEST_PRIVATE_KEY_PEM = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDfn1nKQshOSj8xw44oC2klFWSNLmK3BnHONCJ1bZfq0EQ5gIfg
tlvB+Px8Ya+VS3OnK7Cdi4iU1fxO9ktN6c6TjmmmFevk8wIwqLthmCSF3r+3+h4e
ddj7hucMsXWv05QUrCPoL6YUUz7Cgpz7ra24rpAmK5z7lsV+f3BEvXkrUQIDAQAB
AoGAC0G3QGI6OQ6tvbCNYGCqq043YI/8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhs
kURaDwk4+8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh/
xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0CQQD13LrBTEDR44ei
lQ/4TlCMPO5bytd1pAxHnrqgMnWovSIPSShAAH1feFugH7ZGu7RoBO7pYNb6N3ia
C1idc7yjAkEA6Nfc6c8meTRkVRAHCF24LB5GLfsjoMB0tOeEO9w9Ous1a4o+D24b
AePMUImAp3woFoNDRfWtlNktOqLel5PjewJBAN9kBoA5o6/Rl9zeqdsIdWFmv4DB
5lEqlEnC7HlAP+3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhcCQQDb
W0mOp436T6ZaELBfbFNulNLOzLLi5YzNRPLppfG1SRNZjbIrvTIKVL4N/YxLvQbT
NrQw+2OdQACBJiEHsdZzAkBcsTk7frTH4yGx0VfHxXDPjfTj4wmD6gZIlcIr9lZg
4H8UZcVFN95vEKxJiLRjAmj6g273pu9kK4ymXNEjWWJn
-----END RSA PRIVATE KEY-----"""
MIICXQIBAAKBgQDfn1nKQshOSj8xw44oC2klFWSNLmK3BnHONCJ1bZfq0EQ5gIfg
tlvB+Px8Ya+VS3OnK7Cdi4iU1fxO9ktN6c6TjmmmFevk8wIwqLthmCSF3r+3+h4e
ddj7hucMsXWv05QUrCPoL6YUUz7Cgpz7ra24rpAmK5z7lsV+f3BEvXkrUQIDAQAB
AoGAC0G3QGI6OQ6tvbCNYGCqq043YI/8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhs
kURaDwk4+8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh/
xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0CQQD13LrBTEDR44ei
lQ/4TlCMPO5bytd1pAxHnrqgMnWovSIPSShAAH1feFugH7ZGu7RoBO7pYNb6N3ia
C1idc7yjAkEA6Nfc6c8meTRkVRAHCF24LB5GLfsjoMB0tOeEO9w9Ous1a4o+D24b
AePMUImAp3woFoNDRfWtlNktOqLel5PjewJBAN9kBoA5o6/Rl9zeqdsIdWFmv4DB
5lEqlEnC7HlAP+3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhcCQQDb
W0mOp436T6ZaELBfbFNulNLOzLLi5YzNRPLppfG1SRNZjbIrvTIKVL4N/YxLvQbT
NrQw+2OdQACBJiEHsdZzAkBcsTk7frTH4yGx0VfHxXDPjfTj4wmD6gZIlcIr9lZg
4H8UZcVFN95vEKxJiLRjAmj6g273pu9kK4ymXNEjWWJn
-----END RSA PRIVATE KEY-----"""


class DockerConfig(_Config): # pylint: disable=too-few-public-methods
Expand Down
3 changes: 2 additions & 1 deletion compliance-api/src/compliance_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
"""This exports all of the models and schemas used by the application."""

from .db import db, ma, migrate
from .user import User
from .position import Position
from .staff_user import PERMISSION_MAP, PermissionEnum, StaffUser
37 changes: 21 additions & 16 deletions compliance-api/src/compliance_api/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,29 @@
"""Super class to handle all operations related to base model."""
from datetime import datetime

from sqlalchemy import Column
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import Boolean, Column, DateTime, String

from .db import db


TENANT_ID = 'tenant_id'


class BaseModel(db.Model):
"""This class manages all of the base model functions."""

__abstract__ = True

created_date = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
updated_date = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=True)

@declared_attr
def created_by(cls): # pylint:disable=no-self-argument, no-self-use, # noqa: N805
"""Return foreign key for created by."""
return Column(db.String(50))
created_date = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_date = Column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=True
)
created_by = Column(String(100), nullable=False)
updated_by = Column(String(100), nullable=True)
is_active = Column(Boolean, default=True, server_default="t", nullable=False)
is_deleted = Column(Boolean, default=False, server_default="f", nullable=False)

@declared_attr
def updated_by(cls): # pylint:disable=no-self-argument, no-self-use, # noqa: N805
"""Return foreign key for modified by."""
return Column(db.String(50))
@classmethod
def get_all(cls):
"""Fetch list of users by access type."""
return cls.query.all()

@classmethod
def find_by_id(cls, identifier: int):
Expand Down Expand Up @@ -67,6 +64,14 @@ def save(self):
db.session.flush()
db.session.commit()

def update(self, payload: dict, commit=True):
"""Update and commit."""
for key, value in payload.items():
if key != "id":
setattr(self, key, value)
if commit:
self.commit()

def delete(self):
"""Delete and commit."""
db.session.delete(self)
Expand Down
Loading

0 comments on commit 466a004

Please sign in to comment.