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

Develop #27

Merged
merged 16 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
def get_session():
# Initialize
config_file = 'config.global.py'
db_params = configParser.getConfig('database_parameters', config_file)

url = configParser.getConfig('database_parameters', config_file)['database_url']
engine = create_engine(url)
url = db_params['database_url']
pool_size = int(db_params.get('pool_size', 25))
max_overflow = int(db_params.get('max_overflow', 5))
engine = create_engine(url, pool_size=pool_size, max_overflow=max_overflow)

with Session(engine) as session:
yield session
3 changes: 1 addition & 2 deletions app/logger/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
from app.utils import configParser
from logging.handlers import TimedRotatingFileHandler

FORMATTER = logging.Formatter("""%(asctime)s - %(name)s - %(levelname)s -
%(message)s""")
FORMATTER = logging.Formatter("""%(asctime)s - %(name)s - %(levelname)s - %(message)s""")
LOG_FILE = "{0}/{1}".format(configParser.getConfig('logging', 'config.global.py')['folder'],
configParser.getConfig('logging', 'config.global.py')['file'])
LEVEL = configParser.getConfig('logging', 'config.global.py')['level']
Expand Down
13 changes: 8 additions & 5 deletions app/routers/authenticate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pprint import pprint
from app.logger import log
from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException, status, Security, Request
Expand All @@ -18,13 +19,15 @@
# responses={404: {"description": "Not found"}},
)

logger = log.get_logger("authenticate")

def initializeAuthOb():
config_file = 'config.' + g.tenant + '.' + g.environment + '.py'
oidc_config = configParser.getConfig('oidc_client', config_file)
oauth = OAuth()

oauth.register(
'rciam',
g.tenant + '.' + g.environment + '.rciam',
client_id=oidc_config['client_id'],
client_secret=oidc_config['client_secret'],
server_metadata_url=oidc_config['issuer'] + "/.well-known/openid-configuration",
Expand All @@ -43,7 +46,7 @@ async def login_endpoint(
request: Request,
oauth_ob= Depends(initializeAuthOb),
server_config= Depends(getServerConfig)):
rciam = oauth_ob.create_client('rciam')
rciam = oauth_ob.create_client(g.tenant + '.' + g.environment + '.rciam')
redirect_uri = server_config['protocol'] + "://" + server_config['host'] + server_config['api_path'] + "/auth"
return await rciam.authorize_redirect(request, redirect_uri)

Expand All @@ -68,7 +71,7 @@ async def authorize_rciam(
response = RedirectResponse(url=urllib.parse.unquote(login_start_url))
response.delete_cookie("login_start")

rciam = oauth_ob.create_client('rciam')
rciam = oauth_ob.create_client(g.tenant + '.' + g.environment + '.rciam')
try:
token = await rciam.authorize_access_token(request)
except OAuthError as error:
Expand Down Expand Up @@ -119,7 +122,7 @@ async def authorize_rciam(

# Authorization
authorize_file = 'authorize.' + g.tenant + '.' + g.environment + '.py'
permissions = permissionsCalculation(authorize_file, user_info_data)
permissions = permissionsCalculation(logger, authorize_file, user_info_data)
permissions_json = json.dumps(permissions).replace(" ", "").replace("\n", "")

# Set the permissions cookie.
Expand All @@ -145,7 +148,7 @@ async def logout(
oauth_ob= Depends(initializeAuthOb),
server_config=Depends(getServerConfig)
):
rciam = oauth_ob.create_client('rciam')
rciam = oauth_ob.create_client(g.tenant + '.' + g.environment + '.rciam')
metadata = await rciam.load_server_metadata()
# todo: Fix this after we complete the multitenacy
redirect_uri = server_config['protocol'] + "://" + server_config['client'] +"/metrics"
Expand Down
8 changes: 4 additions & 4 deletions app/routers/communities.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async def read_min_date_communities(
WHERE tenenv_id={0}
""".format(tenenv_id)).one()
return min_date
@router.get("/members_bystatus/")
@router.get("/members_bystatus")
async def read_members_bystatus(
*,
session: Session = Depends(get_session),
Expand Down Expand Up @@ -107,7 +107,7 @@ async def read_communities(
return communities


@router.get("/communities/")
@router.get("/communities")
async def read_community(
*,
session: Session = Depends(get_session),
Expand All @@ -117,7 +117,7 @@ async def read_community(
if community_id:
sql_subquery = 'id={0} and'.format(community_id)
community = session.exec("""
SELECT * FROM community_info WHERE {0} tenenv_id={1}
SELECT * FROM community_info WHERE {0} tenenv_id={1} ORDER BY name ASC
""".format(sql_subquery, tenenv_id)).all()
# statement = select(Community).options(selectinload(Community.community_info))
# result = session.exec(statement)
Expand All @@ -127,7 +127,7 @@ async def read_community(
return community


@router.get("/communities_info/", response_model=List[Community_InfoRead])
@router.get("/communities_info", response_model=List[Community_InfoRead])
async def read_communities_info(
*,
session: Session = Depends(get_session),
Expand Down
34 changes: 27 additions & 7 deletions app/utils/globalMethods.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pprint import pprint
from app.logger import log
import requests as reqs
from fastapi import Depends, FastAPI, HTTPException, Query, Request, HTTPException, status, Response
import json, jwt
Expand All @@ -10,19 +10,28 @@

# https://www.fastapitutorial.com/blog/class-based-dependency-injection/
class AuthNZCheck:
logger = log.get_logger("AuthNZCheck")

def __init__(self, tag: str = "", skip: bool = False):
self.skip = skip
self.tag = tag
self.oauth = OAuth()

async def __call__(self, request: Request, response: Response):
self.logger.debug("""=============== Request Context =================""")
self.logger.debug("""{0}.{1}: Request Url: {2}""" . format(g.tenant, g.environment, request.url))
self.logger.debug("""{0}.{1}: Request Headers: {2}""" . format(g.tenant, g.environment, request.headers))

# config
authorize_file = 'authorize.' + g.tenant + '.' + g.environment + '.py'
config_file = 'config.' + g.tenant + '.' + g.environment + '.py'
oidc_config = configParser.getConfig('oidc_client', config_file)

self.logger.debug("""{0}.{1}: Authorize Config File Name: {2}""".format(g.tenant, g.environment, authorize_file))
self.logger.debug("""{0}.{1}: Config File Name: {2}""".format(g.tenant, g.environment, config_file))

self.oauth.register(
'rciam',
g.tenant + '.' + g.environment + '.rciam',
client_id=oidc_config['client_id'],
client_secret=oidc_config['client_secret'],
server_metadata_url=oidc_config['issuer'] + "/.well-known/openid-configuration",
Expand All @@ -33,24 +42,29 @@ async def __call__(self, request: Request, response: Response):

# permissions calculation
access_token = request.headers.get('x-access-token')
rciam = self.oauth.create_client('rciam')
rciam = self.oauth.create_client(g.tenant + '.' + g.environment + '.rciam')
metadata = await rciam.load_server_metadata()

headers = {'Authorization': f'Bearer {access_token}'}

resp = reqs.get(metadata['userinfo_endpoint'], headers=headers)
self.logger.debug("""{0}.{1}: User Info Endpoint Response: {2}""" . format(g.tenant, g.environment, resp))

# Authentication
if resp.status_code == 401:
# For now we skip logins and dashboard routes
if (self.tag == 'logins' or self.tag == 'dashboard') and self.skip:
permissions = permissionsCalculation(authorize_file)
permissions = permissionsCalculation(self.logger, authorize_file)
permissions_json = json.dumps(permissions).replace(" ", "").replace("\n", "")
# pprint(permissions_json)
response.headers["X-Permissions"] = permissions_json
response.headers["X-Authenticated"] = "false"
response.headers["X-Redirect"] = "false"
return

self.logger.debug("""{0}.{1}: Unauthorized request to User Info endpoint""")
self.logger.debug("""{0}.{1}: Response headers: {2}""" . format(g.tenant, g.environment, resp.headers))
self.logger.debug("""{0}.{1}: Response body: {2}""" . format(g.tenant, g.environment, resp.json()))
raise HTTPException(
status_code=401,
detail="Authentication failed",
Expand All @@ -66,10 +80,13 @@ async def __call__(self, request: Request, response: Response):
data = resp.json()
except Exception as er:
# TODO: Log here
self.logger.error("""{0}.{1}: error: {2}""".format(g.tenant, g.environment, er))
raise HTTPException(status_code=500)

self.logger.debug("""{0}.{1}: User Info Response: {2}""" . format(g.tenant, g.environment, data))
# Authorization
permissions = permissionsCalculation(authorize_file, data)
permissions = permissionsCalculation(self.logger, authorize_file, data)
self.logger.debug("""{0}.{1}: permissions: {2}""".format(g.tenant, g.environment, permissions))
permissions_json = json.dumps(permissions).replace(" ", "").replace("\n", "")

# Add the permission to a custom header field
Expand All @@ -82,7 +99,7 @@ async def __call__(self, request: Request, response: Response):
HTTPException(status_code=403)


def permissionsCalculation(authorize_file, user_info=None):
def permissionsCalculation(logger, authorize_file, user_info=None):
entitlements_config = configParser.getConfig('entitlements', authorize_file)
user_entitlements = {}
if user_info is not None:
Expand All @@ -94,6 +111,9 @@ def permissionsCalculation(authorize_file, user_info=None):
'administrator': False
}

logger.debug("""{0}.{1}: Entitlements Config: {2}""".format(g.tenant, g.environment, entitlements_config))
logger.debug("""{0}.{1}: User Entitlements Config: {2}""".format(g.tenant, g.environment, user_entitlements))

for ent, role in entitlements_config.items():
if user_entitlements is not None and ent in user_entitlements:
# Reset the default anonymous role
Expand All @@ -103,7 +123,7 @@ def permissionsCalculation(authorize_file, user_info=None):
for item_role in role.split(","):
roles[item_role] = True

# pprint(roles)
logger.debug("""{0}.{1}: roles: {2}""".format(g.tenant, g.environment, roles))

actions = {
'dashboard': {
Expand Down
1 change: 1 addition & 0 deletions javascript/src/Pages/Communities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Communities = () => {
[tenenvKey, {tenantId: tenant, environment: environment}],
getTenenv, {
retry: 0,
refetchOnWindowFocus: false
})

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ const CommunitiesDataTable = ({tenenvId}) => {
// We only keep the first date because the backend returns the dataset sorted and we only care about the
// min of the min dates.
if (minDate == undefined || minDate == "") {
console.log(minDateCommunities?.data?.min_date)
setMinDate(!!minDateCommunities?.data?.min_date ? new Date(minDateCommunities?.data?.min_date) : null)
}
$("#table-community").DataTable().destroy()
Expand Down
2 changes: 0 additions & 2 deletions javascript/src/components/Users/registeredUsersDataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ const RegisteredUsersDataTable = ({
params: params
}])
queryClient.refetchQueries([minDateRegisteredUsersKey, {params:{tenenv_id: tenenvId}}])
console.log(params)
} catch (error) {
// todo: Here we can handle any authentication or authorization errors
console.log(error)
Expand All @@ -90,7 +89,6 @@ const RegisteredUsersDataTable = ({

// Construct the data required for the datatable
useEffect(() => {
console.log(registeredUsersPerCountryGroup)
const perPeriod = registeredUsersPerCountryGroup?.data?.map(user => ({
"Date": !!user?.range_date ? convertDateByGroup(new Date(user?.range_date), groupBy): null,
"Number of Registered Users": user?.count,
Expand Down
3 changes: 1 addition & 2 deletions javascript/src/utils/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const deleteCookie = (name, path, domain) => {

const handleError = (error) => {
console.log('error', error)
// debugger
if (error?.response?.status == 401
&& error?.response?.headers?.['x-authenticated'] == "false"
&& error?.response?.headers?.['x-redirect'] == "true"
Expand All @@ -54,7 +53,7 @@ const client = axios.create({
"Content-type": "application/json",
'x-access-token': `${getCookie('atoken')}`,
'x-tenant': `${config.tenant}`,
'x-environment': `${config.environment}`,
'x-environment': `${config.environment}`
},
validateStatus: function (status) {
return status >= 200 && status < 300; // default
Expand Down
Loading
Loading