Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
cd3a870
Added custom property for hide sensitive data
shuhaib-aot Feb 26, 2024
1a02926
added req.params.submisssionId and formid to load previous data when …
shuhaib-aot Feb 27, 2024
afa1e90
added bundle forms sensitiviy
shuhaib-aot Apr 25, 2024
54ba5c2
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot Apr 25, 2024
bbfa90b
Changed version 6.1.0-alpha
shuhaib-aot Apr 25, 2024
9e98984
Merge pull request #80 from shuhaib-aot/hide-sensitive-data
shuhaib-aot May 3, 2024
e572d81
[Added] formIds query parameter in /form path and restricted for only…
shuhaib-aot May 9, 2024
84eb6fc
Merge pull request #85 from shuhaib-aot/FWF-3283
shuhaib-aot May 10, 2024
1d6fcdb
displayFor changed to displayForRole
shuhaib-aot May 13, 2024
22a36e1
Fixed custom role issue
shuhaib-aot May 14, 2024
d9544f2
Merge pull request #86 from shuhaib-aot/change-displayFor-typo
shuhaib-aot May 14, 2024
227a0e4
Merge pull request #87 from shuhaib-aot/fix-custom-role
shuhaib-aot May 14, 2024
44699ad
[Removed] duplicate code
shuhaib-aot May 14, 2024
4cc3178
Merge pull request #88 from AOT-Technologies/shuhaib-aot-patch-2
arun-s-aot May 14, 2024
61a556f
Fwf:3197 [Feature] fixed authorization part to fetch bundle submissio…
shuhaib-aot May 22, 2024
ba5db2f
Update handleFormsList.js
shuhaib-aot May 29, 2024
b29c858
Update handleFormsList.js
shuhaib-aot May 29, 2024
bdb0e2e
Added application id and status in bundle submission data
shuhaib-aot Jun 13, 2024
5623b10
Merge pull request #89 from shuhaib-aot/feature/hide-bundle-sensitive…
shuhaib-aot Jun 14, 2024
765eff1
Added additional check for external token
shuhaib-aot Jun 14, 2024
e4341e9
Merge pull request #90 from shuhaib-aot/feature/FWF-3196-bundle-sensi…
shuhaib-aot Jun 14, 2024
b8a57fe
Update handleFormsList.js
shuhaib-aot Jul 1, 2024
6705818
Ignored role mapping to form access when a new role create (#91)
shuhaib-aot Jul 9, 2024
5713603
Modified version to 7.0.0-alpha (#92)
shuhaib-aot Jul 31, 2024
ab5fdbd
Added new endpoint 'forms/search' for listing forms by path or name o…
shuhaib-aot Aug 22, 2024
600b2fa
Feature/adde select in search endpoint (#94)
shuhaib-aot Aug 29, 2024
f962f79
Update VERSION
shuhaib-aot Jan 2, 2025
bef8f8d
Added Change log fof 7.0.0
shuhaib-aot Jan 7, 2025
dd0c24e
Modified typo
shuhaib-aot Jan 9, 2025
d33946c
Added Change log fof 7.0.0 (#96)
shuhaib-aot Jan 9, 2025
daf40fe
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot Jan 10, 2025
aa35a8f
Bugfix/fix sonarcloud (#97)
shuhaib-aot Jan 10, 2025
8d86d0f
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot Jan 20, 2025
bd9b705
Feature/formlisting api by formids and added new api for submission l…
shuhaib-aot Feb 26, 2025
6bdb293
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot May 15, 2025
180bae8
Bugfix/fwf 4652 (#99)
shuhaib-aot May 16, 2025
b1bea30
Update formsflow-forms-cd.yml
shuhaib-aot May 16, 2025
b48a226
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot Jun 25, 2025
b4120ef
Feature/fwf 4299 added tenant key in logs (#100)
shuhaib-aot Jun 26, 2025
e7ca7b9
Merge branch 'develop' of https://github.com/AOT-Technologies/formio …
shuhaib-aot Jun 26, 2025
bb9c1db
Version changed and change log updated
shuhaib-aot Jun 26, 2025
12c4acd
Merge branch 'formsflow-forms' of https://github.com/AOT-Technologies…
shuhaib-aot Jun 26, 2025
ed1e53d
Merge pull request #102 from shuhaib-aot/master-to-develo-with-versio…
shuhaib-aot Jun 26, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/formsflow-forms-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Cache Docker layers
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ matrix.name }}-${{ github.sha }}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ To know more about form.io, go to <https://form.io>.
|`MULTI_TENANCY_ENABLED`|To enable multit tenancy |true / false|`false`
|`FORMIO_DEFAULT_PROJECT_URL`:triangular_flag_on_post:|forms-flow-forms default url||`http://{your-ip-address}:3001`
|`FORMIO_JWT_SECRET`|forms-flow-forms jwt secret| |`--- change me now ---`|
|`FORMIO_JWT_EXPIRE`|forms-flow-forms jwt expire time| |`240`|

**Additionally, you may want to change these**

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v7.0.0
v7.1.0
8 changes: 7 additions & 1 deletion formsflow-forms-ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
- `Untested Features`: Newly introduced features or components that are yet to be thoroughly tested.
- `Upcoming Features`: Planned features or enhancements that will be available in future releases.
- `Known Issues`: Existing issues or problems that are acknowledged and will be addressed in subsequent updates.


# Version 7.1.0
### Added
- Added FORMIO_JWT_EXPIRE env for handling token expire time
- Added tenant_key in logger
- Added New /submissions POST API Allows listing submissions by passing submissionIds in the request body. Ensures access control by verifying form access before returning submissions.

# Version 7.0.0
### Added
- Added the formIds query parameter in the /form endpoint to retrieve form components, accessible only to admin users.
Expand Down
7 changes: 4 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ module.exports = function(config) {
router.formio.config.schema = require('./package.json').schema;

router.formio.log = (event, req, ...info) => {
const tenantKey = req.token && req.token.tenantKey;
const result = router.formio.hook.alter('log', event, req, ...info);
logger.info(event, ...info);
logger.info(event,{tenantKey: tenantKey, info: info});
if (result) {
log(event, ...info);
}
Expand Down Expand Up @@ -213,12 +214,10 @@ module.exports = function(config) {
if (!router.formio.hook.invoke('init', 'config', router.formio)) {
router.use('/config.json', router.formio.middleware.configHandler);
}

// Authorize all urls based on roles and permissions.
if (!router.formio.hook.invoke('init', 'perms', router.formio)) {
router.use(router.formio.middleware.permissionHandler);
}

let mongoUrl = config.mongo;
let mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {};
if (!mongoConfig.hasOwnProperty('connectTimeoutMS')) {
Expand Down Expand Up @@ -299,6 +298,8 @@ module.exports = function(config) {
const formListingByPathNameTitle = require('./src/resources/FormListingByPathTitleName');
router.get('/forms/search',formListingByPathNameTitle.getFormsByTitleOrPathOrName(router));

const submissionList = require('./src/resources/SubmissionList').getAllSubmissions(router);
router.post('/submissions',router.formio.middleware.tokenVerify,submissionList);
// Return the form components.
router.get('/form/:formId/components', function(req, res, next) {
router.formio.resources.form.model.findOne({_id: req.params.formId}, function(err, form) {
Expand Down
1 change: 1 addition & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
FORMIO_DEFAULT_PROJECT_URL=http://{your-ip-address}:3001

#FORMIO_JWT_SECRET=--- change me now ---
#FORMIO_JWT_EXPIRE=240
#MULTI_TENANCY_ENABLED=false
#NO_INSTALL=1

Expand Down
8 changes: 4 additions & 4 deletions src/authentication/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ module.exports = (router) => {
} = jwtConfig;

// changed the secret and expire to the env


const expireMinutes = Number(process.env.FORMIO_JWT_EXPIRE) || expireTime || 240;
const expiresInSeconds = expireMinutes * 60;
return jwt.sign(payload, customSecret ||process.env.FORMIO_JWT_SECRET||secret , {
expiresIn: (expireTime ||240) * 60,
expiresIn: expiresInSeconds,
});
};
// Number(process.env.FORMIO_JWT_EXPIRE)|| todo

/**
* Checks to see if a decoded token is allowed to access this path.
* @param req
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/alias.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ module.exports = function(router) {
// If this is normal request, then pass this middleware.
/* eslint-disable no-useless-escape */
//this custom endpoint written for formsflow
const customEndpoint = new Set(["checkpoint", "forms/search"])
const customEndpoint = new Set(["checkpoint", "forms/search", "submissions"])
if (!alias || alias.match(/^(form$|form[\?\/])/) || alias === 'spec.json' || customEndpoint.has(alias) || alias === 'config.json') {
return next();
}
Expand Down
42 changes: 31 additions & 11 deletions src/middleware/handleFormsList.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,50 @@
"use strict";

const { ObjectId } = require('../util/util');

require('dotenv').config();

/**
* Middleware function to handle /form route.
*
* @param router
*
* @returns {Function}
*/

module.exports = function (router) {
return function (req, res, next) {
// If the request is for the /form route, and it is a GET request, then we need to check
if(req.path === "/form" && req.method === "GET"){
if(!req.isAdmin) return res.sendStatus(401)
if(req.query.formIds){
req.query._id = { $in: req.query.formIds.split(",")}
delete req.query.formIds
return async function (req, res, next) {
// Only check access for GET requests to /form route
if (req.path === "/form" && req.method === "GET") {
const query = {};

if(!req.user?.roles){
return res.sendStatus(401);
}
// If specific formIds are provided, include them in query
if (req.query.formIds) {
query._id = { $in: req.query.formIds.split(",") };
delete req.query.formIds;
}
if(!req.isAdmin){
// Ensure role-based access check
query.access = {
$elemMatch: {
'type': 'read_all',
'roles': { $in: req.user.roles }
}
}
}

if(process.env.MULTI_TENANCY_ENABLED == "true" && !req.isAdmin){
if(!req.token?.tenantKey){
return res.json([])
return res.sendStatus(401);
}
req.query.tenantKey = req.token.tenantKey
}
// Merge any additional query parameters
req.query = { ...query, ...req.query };
}
next()
next();
};
};


8 changes: 6 additions & 2 deletions src/middleware/permissionHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -727,9 +727,13 @@ module.exports = function(router) {

// Check for whitelisted paths.
let skip = false;
const url = req.url.split('?')[0];
// skipping permission check for this route and it will do manually where route define
if(req.method === "POST" && url == "/submissions"){
skip = true;
}
if (req.method === 'GET') {
const whitelist = ['/health', '/current', '/logout', '/access', '/token', '/recaptcha'];
const url = req.url.split('?')[0];
const whitelist = ['/health', '/current', '/logout', '/access', '/token', '/recaptcha'];
skip = _.some(whitelist, function(path) {
if ((url === path) || (url === hook.alter('path', path, req))) {
return true;
Expand Down
61 changes: 61 additions & 0 deletions src/resources/SubmissionList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const { ObjectId } = require("../util/util");

module.exports = {
getAllSubmissions: (router) =>
async function (req, res, next) {
try {
const userRoles = req.user.roles.map((id) => ObjectId(id));

// submissionIds taking from req.body
const submissionIds = req.body.submissionIds
? req.body.submissionIds.map((id) => ObjectId(id.trim()))
: [];

const pipeline = [
{
$match: {
_id: { $in: submissionIds }, // Filter by submission IDs
},
},
{
$lookup: {
from: "forms",
localField: "form",
foreignField: "_id",
as: "formDetails",
},
},
{
$unwind: "$formDetails",
},
{
$match: {
$or: [
{
"formDetails.submissionAccess": {
$elemMatch: {
type: "read_all",
roles: { $in: userRoles },
},
},
},
{
owner: ObjectId(req.user._id), // User must be the owner for read_own
},
],
},
},
{
"$project":{
formDetails:0
}
}
];
// mongo query execute
const result = await router.formio.resources.submission.model.aggregate(pipeline)
res.json(result)
} catch (error) {
next(error);
}
},
};
28 changes: 23 additions & 5 deletions src/util/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,29 @@ const logFolder = process.env.LOG_FOLDER || "./logs"
const archivedFolder = logFolder+"/archived"
const logFile = "formio-%DATE%.log"
//Using the printf format.
const customFormat = printf(({ level, label, timestamp, ...meta}) => {
const args = meta[Symbol.for('splat')];
if(args) meta.message = util.format(meta.message, ...args);
meta.message = formatObjectsAndArrays(meta.message);
return `${timestamp} [${label}] ${level}: ${meta.message}`;

const customFormat = printf(({ level, label, timestamp, ...meta }) => {
const args = meta[Symbol.for('splat')] || [];
let tenantKey;
let formattedArgs = args;

// Extract tenantKey if first arg is an object with tenantKey
if (args[0] && typeof args[0] === 'object' && args[0].tenantKey) {
tenantKey = args[0].tenantKey;
formattedArgs = args[0].info || [];
}

// Format the message
if (formattedArgs.length > 0) {
meta.message = util.format(meta.message, ...formattedArgs);
}

meta.message = formatObjectsAndArrays(meta.message);

// Build the log string
const tenantPart = tenantKey ? ` [tenantKey: ${tenantKey}]` : '';
const log = `${timestamp} [${label}]${tenantPart} ${level}: ${meta.message}`;
return log;
});


Expand Down
Loading