Skip to content

Commit

Permalink
Merge pull request AOT-Technologies#2165 from shuhaib-aot/Feature/FWF…
Browse files Browse the repository at this point in the history
…-3316-permission-matrix

resolve conflict
  • Loading branch information
shuhaib-aot authored Jul 25, 2024
2 parents 630ef30 + c045dce commit 78249e7
Show file tree
Hide file tree
Showing 32 changed files with 217 additions and 96 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

Mark items as `Added`, `Changed`, `Fixed`, `Modified`, `Removed`, `Untested Features`, `Upcoming Features`, `Known Issues`

## 6.1.0

`Added`

**forms-flow-bpm**
* Added support to fetch secrets from Vault.
* Added environment variables
`VAULT_ENABLED`, `VAULT_URL`, `VAULT_TOKEN`, `VAULT_PATH`, `VAULT_SECRET` to support Vault.

## 6.0.2 - 2024-06-05

`Added`
Expand Down
2 changes: 1 addition & 1 deletion deployment/docker/sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,6 @@ CUSTOM_SUBMISSION_URL=http://{your-ip-address}:{port}
# Vault configuration
# VAULT_ENABLED=false
# VAULT_URL=http://{your-ip-address}:8200
# VAULT_TOKEN=<PASSWORD>
# VAULT_TOKEN=<token>
# VAULT_PATH=
# VAULT_SECRET=
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""This exports all of the services used by the application."""

from formsflow_api_utils.services.external.formio import FormioService

from formsflow_api_utils.services.external.custom_submission import CustomSubmissionService
__all__ = [
"FormioService",
"CustomSubmissionService"
]
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""This exports all of the external communication services used by the application."""

from .formio import FormioService
from .custom_submission import CustomSubmissionService
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import requests
from flask import current_app

from formsflow_api_utils.exceptions import BusinessException, ExternalError
from formsflow_api_utils.utils import HTTP_TIMEOUT
from typing import Any

class CustomSubmissionService:

def __init__(self) -> None:
self.base_url = current_app.config.get("CUSTOM_SUBMISSION_URL")

def __get_headers(self, token: str) -> dict:
"""Returns the headers for the request."""
return {"Authorization": token, "Content-Type": "application/json"}

def fetch_submission_data(self, token: str, form_id: str, submission_id: str) -> Any:
"""Returns the submission data from the form adapter."""
if not self.base_url:
raise BusinessException("Base URL for custom submission is not configured.")

submission_url = f"{self.base_url}/form/{form_id}/submission/{submission_id}"
current_app.logger.debug(f"Fetching custom submission data: {submission_url}")
headers = self.__get_headers(token)

try:
response = requests.get(submission_url, headers=headers, timeout=HTTP_TIMEOUT)
current_app.logger.debug(f"Custom submission response code: {response.status_code}")
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
current_app.logger.error(f"Error fetching custom submission data: {str(e)}")
raise ExternalError("Failed to fetch custom submission data.") from e
10 changes: 7 additions & 3 deletions forms-flow-api-utils/src/formsflow_api_utils/utils/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from flask import current_app
from redis.client import Redis
import json

import os
from redis import RedisCluster as rc

class RedisManager:
"""
Expand All @@ -28,11 +29,15 @@ def get_client(cls, app=None) -> Redis:
Raises:
Exception: If the Redis client has not been initialized and no app context is provided.
"""
redis_cluster = os.getenv('REDIS_CLUSTER', 'false').lower() == 'true'
if cls._redis_client is None:
if app is None:
app = current_app
redis_url = app.config.get("REDIS_URL")
cls._redis_client = redis.StrictRedis.from_url(redis_url)
if redis_cluster:
cls._redis_client = rc.from_url(redis_url)
else:
cls._redis_client = redis.StrictRedis.from_url(redis_url)
app.logger.info("Redis client initiated successfully")
return cls._redis_client

Expand Down Expand Up @@ -75,4 +80,3 @@ def get(cls, key):
return json.loads(val_json) # Deserialize the JSON string back into Python object
else:
return None

2 changes: 1 addition & 1 deletion forms-flow-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pyparsing==3.1.2
python-dotenv==1.0.1
python-jose==3.3.0
pytz==2024.1
redis==5.0.3
redis==5.0.5
referencing==0.34.0
requests==2.31.0
rpds-py==0.18.0
Expand Down
3 changes: 2 additions & 1 deletion forms-flow-api/sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ FORMIO_ROOT_PASSWORD=changeme
#CONFIGURE_LOGS=true

#Redis configuration
REDIS_URL=redis://{your-ip-address}:6379/0
REDIS_URL=redis://{your-ip-address}:6379/0
REDIS_CLUSTER=false
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ def get_application_history(cls, application_id: int):
cls.submitted_by,
)
)

@classmethod
def get_application_history_by_id(cls, application_id: int):
"""Find application history by id."""
return cls.query.filter(cls.application_id == application_id).first()
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,11 @@ def post(application_id):
"""Post a new history entry using the request body."""
application_history_json = request.get_json()

# try:
application_history_schema = ApplicationHistorySchema()
dict_data = application_history_schema.load(application_history_json)
dict_data["application_id"] = application_id
application_history = ApplicationHistoryService.create_application_history(
data=dict_data
data=dict_data, application_id=application_id
)

response, status = (
Expand Down
14 changes: 12 additions & 2 deletions forms-flow-api/src/formsflow_api/services/application_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,26 @@
from flask import current_app
from formsflow_api_utils.utils import get_form_and_submission_id_from_form_url

from formsflow_api.models import ApplicationHistory
from formsflow_api.models import Application, ApplicationHistory
from formsflow_api.schemas import ApplicationHistorySchema


class ApplicationHistoryService:
"""This class manages application service."""

@staticmethod
def create_application_history(data):
def create_application_history(data, application_id):
"""Create new application history."""
# Replace service-account with application creator in initial history.
if data.get("submitted_by") and data.get("submitted_by").startswith(
"service-account"
):
application_history = ApplicationHistory.get_application_history_by_id(
application_id
)
if not application_history:
application = Application.find_by_id(application_id)
data["submitted_by"] = application.created_by
(form_id, submission_id) = get_form_and_submission_id_from_form_url(
data["form_url"]
)
Expand Down
26 changes: 26 additions & 0 deletions forms-flow-api/tests/unit/api/test_application_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from formsflow_api_utils.utils import VIEW_SUBMISSIONS

from tests.utilities.base_test import get_token
from formsflow_api.models import Application


def get_history_create_payload():
Expand Down Expand Up @@ -71,3 +72,28 @@ def test_application_history_get_un_authorized(app, client, session, jwt):
# sending get request withouttoken
rv = client.get("/application/1/history")
assert rv.status_code == 401


def create_application_history_service_account(app, client, session, jwt):
"""Tests if the initial application history created with a service account replaced by application creator."""
application = Application()
application.created_by = "client"
application.application_status = "New"
application.form_process_mapper_id = 1
application.submission_id = "2345"
application.latest_form_id = "1234"
application.save()

payload = {
"applicationId": 1,
"applicationStatus": "New",
"formUrl": "http://testsample.com/form/23/submission/3423",
"submittedBy": "service-account-bpmn",
}
token = get_token(jwt)
headers = {"Authorization": f"Bearer {token}", "content-type": "application/json"}
new_entry = client.post(
"/application/1/history", headers=headers, json=payload
)
assert new_entry.status_code == 201
assert new_entry.submitted_by == "client"
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_create_application_history(app, client, session):
}
payload["application_id"] = 1222 # sample value
application_history = application_history_service.create_application_history(
data=payload
data=payload, application_id=1222
)
assert application_history.application_id == 1222
assert application_history.application_status == "Pending"
Expand Down
1 change: 0 additions & 1 deletion forms-flow-documents/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ services:
tty: true # -t
networks:
- forms-flow-webapi-network
- redis

redis:
image: "redis:alpine"
Expand Down
14 changes: 7 additions & 7 deletions forms-flow-idm/keycloak/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ services:
POSTGRES_USER: ${KEYCLOAK_JDBC_USER:-admin}
POSTGRES_PASSWORD: ${KEYCLOAK_JDBC_PASSWORD:-changeme}
ports:
- 5431:5431
- 5431:5432
networks:
- keycloak-server-network

Expand All @@ -33,12 +33,12 @@ services:
- keycloak_custom_data:/keycloak_custom_data
entrypoint: ["/bin/bash", "-c", "/keycloak_custom_data/start-keycloak.sh"]
environment:
- DB_VENDOR=POSTGRES
- DB_ADDR=keycloak-db
- DB_PORT=5432
- DB_DATABASE=${KEYCLOAK_JDBC_DB:-keycloak}
- DB_USER=${KEYCLOAK_JDBC_USER:-admin}
- DB_PASSWORD=${KEYCLOAK_JDBC_PASSWORD:-changeme}
- KC_DB=postgres
- KC_DB_URL_HOST=keycloak-db
- KC_DB_URL_PORT=5432
- KC_DB_URL_DATABASE=${KEYCLOAK_JDBC_DB:-keycloak}
- KC_DB_USERNAME=${KEYCLOAK_JDBC_USER:-admin}
- KC_DB_PASSWORD=${KEYCLOAK_JDBC_PASSWORD:-changeme}
- KEYCLOAK_ADMIN=${KEYCLOAK_ADMIN_USER:-admin}
- KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD:-changeme}
- KEYCLOAK_START_MODE=${KEYCLOAK_START_MODE:-start-dev}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,24 +383,22 @@
@media (max-width: 767px) {

.login-pf body {
background: url("../img/img2.png") no-repeat;
background: none;
/* background-size: cover; */
background-size: 50rem 40rem;
}

#kc-header {
padding-left: 15px;
padding-right: 15px;
float: none;
text-align: left;
}

#kc-header-wrapper {
font-size: 16px;
font-weight: bold;
padding: 20px 60px 0 0;
color: #72767b;
letter-spacing: 0;
padding: 0;
}

div.kc-logo-text {
Expand Down Expand Up @@ -527,10 +525,9 @@
.login-pf-page .card-pf {
/* max-width: none; */
/* margin-left: 0; */
margin-right: 5rem;
padding-top: 0;
border-top: 0;
/* box-shadow: 0 0; */
margin: 0 10px ;
}

.kc-social-grid {
Expand All @@ -541,8 +538,15 @@
.kc-social-grid .kc-social-icon-text {
left: -15px;
}
.card-pf {
padding: 1.5rem ;
}
.login-pf-page {
padding-top: 0 ;
}
}



.login-pf-page .login-pf-signup {
font-size: 15px;
color: #72767b;
Expand Down
8 changes: 6 additions & 2 deletions forms-flow-web/src/components/AccessDenied/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ import { kcServiceInstance } from "../PrivateRoute"; // Import the kcServiceInst
import { ReactComponent as AccessDeniedIcon } from "./AccessDenied.svg";
import './accessDenied.scss';
import { useTranslation } from "react-i18next";
import { BASE_ROUTE } from "../../constants/constants";
import { useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import { MULTITENANCY_ENABLED } from "../../constants/constants";


const AccessDenied = ({ userRoles }) => {
const { t } = useTranslation();
const history = useHistory();
const tenantKey = useSelector((state) => state.tenants?.tenantId);
const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/";


const handleLogout = () => {
const kcInstance = kcServiceInstance();
kcInstance.userLogout();
};

const handleReturn = () => {
history.push(BASE_ROUTE);
history.push(redirectUrl);
};

const showReturnToLogin = userRoles?.length === 0;
Expand Down
4 changes: 2 additions & 2 deletions forms-flow-web/src/components/Dashboard/StatusChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ const ChartForm = React.memo((props) => {
<div className=" d-flex align-items-center justify-content-between">
<div>
<div className="d-flex align-items-center">
<span className="text-primary me-2" >{t("Form Name")} : </span>
<h2 className="text-truncate" style={{ maxWidth: version > 1 ? "500px" : "700px", marginBottom: "30px"}}>{formName}</h2>
<span className="text-primary me-2 mt-2" >{t("Form Name")} : </span>
<h2 className="text-truncate mt-0" style={{ maxWidth: version > 1 ? "500px" : "700px"}}>{formName}</h2>
</div>
<p>
<span className="text-primary" >{t("Latest Version")} :</span>{" "}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { DesignerAccessDenied } from "../../../actions/checkListActions";
import { Translation, useTranslation } from "react-i18next";

// eslint-disable-next-line no-unused-vars
const FileModal = React.memo(({ modalOpen = false, onClose, forms,
pathErrorMessage, isloading }) => {
const FileModal = React.memo(({ modalOpen = false, onClose,
validationErrors, isloading }) => {
const dispatch = useDispatch();
const formUploadList = useSelector(
(state) => state.formCheckList.formUploadFormList
Expand Down Expand Up @@ -77,7 +77,8 @@ const FileModal = React.memo(({ modalOpen = false, onClose, forms,
<Translation>{(t) => t("No forms found")}</Translation>
</div>
)}
<div className="has-error">{pathErrorMessage}</div>
<div className="has-error">{validationErrors?.path}</div>
<div className="has-error">{validationErrors?.name}</div>
</Modal.Body>
<Modal.Footer style={{ justifyContent: `${noAccess ? "space-between" : ''}` }}>
{noAccess && <span className="fileupload-fail">{t("Access restricted by its designer..!")}</span>}
Expand Down
2 changes: 1 addition & 1 deletion forms-flow-web/src/components/Form/Item/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ const View = React.memo((props) => {
className="col-12"
>
<div className="ms-4 me-4">
{isPublic || formStatus === "active" ? (
{(isPublic || (formStatus === "active") ) ? (
<Form
form={form}
submission={submission}
Expand Down
Loading

0 comments on commit 78249e7

Please sign in to comment.