diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc943d71..0dbb7f88 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [4.0.19] - 2023-11-22
+
+### Fixed
+
+- undefined error with json
+
+### Added
+
+- Added AzureAD group filter to limit the number of groups
+
## [4.0.18] - 2023-11-10
### Fixed
@@ -611,7 +621,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow change password for current local user
- Start tracking versions
-[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.18...HEAD
+[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.19...HEAD
+
+[4.0.19]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.18...4.0.19
[4.0.18]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.17...4.0.18
diff --git a/app_versions.gradle b/app_versions.gradle
index df57aeeb..1d069c96 100644
--- a/app_versions.gradle
+++ b/app_versions.gradle
@@ -1,2 +1,2 @@
-ext.version_code = 40018
-ext.version_name = "4.0.18"
+ext.version_code = 40019
+ext.version_name = "4.0.19"
diff --git a/client/package.json b/client/package.json
index b11e90e6..fd7e27c5 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "ansible_forms_vue",
- "version": "4.0.18",
+ "version": "4.0.19",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
diff --git a/client/src/components/Form.vue b/client/src/components/Form.vue
index bde69e04..18c26dd4 100644
--- a/client/src/components/Form.vue
+++ b/client/src/components/Form.vue
@@ -1180,7 +1180,7 @@
// check self references
while(!finishedFlag){
finishedFlag=true
- temp = JSON.parse(JSON.stringify(ref.dynamicFieldDependencies)); // copy dependencies to temp
+ temp = Helpers.deepClone(ref.dynamicFieldDependencies); // copy dependencies to temp
for (const [key, value] of Object.entries(temp)) {
// loop all found dependenies and dig deeper
value.forEach((item,i) => {
@@ -1270,7 +1270,7 @@
// console.log("item = " + value)
// console.log(typeof value)
// console.log(testRegex)
- value = value?.replace(/\n+/g, ' ') // put everything in 1 line.
+ value = value?.replace(/\n+/g, '') // put everything in 1 line.
matches=[...value.matchAll(testRegex)] // force match array
for(match of matches){
// console.log("-> match : " + match[0] + "->" + match[1])
@@ -1843,7 +1843,7 @@
// else just use the formdata
}else{
// deep clone, otherwise weird effects
- outputValue = JSON.parse(JSON.stringify(this.form[item.name]))
+ outputValue = Helpers.deepClone(this.form[item.name])
}
// if no model is given, we assign to the root
if(!outputObject){ // do we need to flatten output ?
@@ -1851,7 +1851,7 @@
}
if(fieldmodel.length==0){
// deep clone = otherwise weird effects
- formdata[item.name]=JSON.parse(JSON.stringify(outputValue))
+ formdata[item.name]=Helpers.deepClone(outputValue)
}else{
fieldmodel.forEach((f)=>{
// convert fieldmodel for actual object
diff --git a/client/src/lib/Helpers.js b/client/src/lib/Helpers.js
index 0ab90d17..1ae942c4 100644
--- a/client/src/lib/Helpers.js
+++ b/client/src/lib/Helpers.js
@@ -28,6 +28,18 @@ var Helpers = {
var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
},
+ deepClone(o){
+ if(o===undefined){
+ return o
+ }
+ try{
+ return (JSON.parse(JSON.stringify(o)))
+ }catch(e){
+ console.error("Failed deepcloning - ",e)
+ return undefined
+ }
+
+ },
evalSandbox(expression){
// local autonumbering
function fnGetNumberedName(names,pattern,value,fillgap=false){
@@ -177,7 +189,8 @@ var Helpers = {
return o
})
}
- }
+ }
+ if(expression)
return eval(expression)
}
diff --git a/client/src/views/AzureAd.vue b/client/src/views/AzureAd.vue
index 169d5ac2..eeca2294 100644
--- a/client/src/views/AzureAd.vue
+++ b/client/src/views/AzureAd.vue
@@ -19,6 +19,7 @@
+
Required API Permissions
@@ -67,7 +68,8 @@
azuread:{
client_id:"",
secret_id:"",
- enable:true
+ enable:true,
+ groupfilter:""
},
settings:{
url:""
diff --git a/client/src/views/Login.vue b/client/src/views/Login.vue
index f02cbf84..6b795b6e 100644
--- a/client/src/views/Login.vue
+++ b/client/src/views/Login.vue
@@ -46,6 +46,7 @@
password: ""
},
azureAdEnabled:false,
+ azureGroupfilter:"",
azureGraphUrl:"https://graph.microsoft.com"
}
},
@@ -56,6 +57,7 @@
.then((result)=>{
if(result.data?.status=='success'){
this.azureAdEnabled=!!result.data.data.output.azureAdEnabled
+ this.azureGroupfilter=result.data.data.output.azureGroupfilter
this.azureGraphUrl=result.data.data.output.azureGraphUrl
if(azuretoken){
this.getGroupsAndLogin(azuretoken)
@@ -69,6 +71,7 @@
})
},
getGroupsAndLogin(azuretoken, url = `${this.azureGraphUrl}/v1.0/me/transitiveMemberOf`, allGroups = []) {
+ var ref=this
const config = {
headers: {
Authorization: `Bearer ${azuretoken}`
@@ -84,6 +87,18 @@
// If there's a nextLink, make a recursive call to get the next page of data
this.getGroupsAndLogin(azuretoken, res.data['@odata.nextLink'], allGroups);
} else {
+ var validRegex=true
+ var regex
+ try{
+ regex = new RegExp(ref.azureGroupfilter, 'g');
+ }catch(e){
+ console.error("MS Entra ID Group filter is not a valid regular expression")
+ validRegex=false
+ }
+ if(validRegex && ref.azureGroupfilter){
+ allGroups = allGroups.filter(x => x.match(regex))
+ console.log("Groups have been filtered")
+ }
// No more nextLink, you have all the groups
axios.post('/api/v1/auth/azureadoauth2/login', { azuretoken, groups:allGroups })
.then((result) => {
diff --git a/server/package.json b/server/package.json
index 0d80370e..e83a22c6 100644
--- a/server/package.json
+++ b/server/package.json
@@ -1,6 +1,6 @@
{
"name": "ansible_forms",
- "version": "4.0.18",
+ "version": "4.0.19",
"repository": {
"type": "git",
"url": "git://github.com/ansibleguy76/ansibleforms.git"
diff --git a/server/schema/forms_schema.json b/server/schema/forms_schema.json
index d081f540..27551412 100644
--- a/server/schema/forms_schema.json
+++ b/server/schema/forms_schema.json
@@ -452,6 +452,7 @@
"recipients"
]
},
+ "hasApproval":{ "type": "boolean"},
"approval": {
"$id": "/approval",
"type": "object",
@@ -487,6 +488,7 @@
"type": "string",
"enum": ["ansible", "awx", "git","multistep"]
},
+ "hasApproval":{ "type": "boolean"},
"approval":{
"$ref": "/approval"
},
diff --git a/server/src/controllers/login.controller.js b/server/src/controllers/login.controller.js
index d4e5fa8d..8db02247 100644
--- a/server/src/controllers/login.controller.js
+++ b/server/src/controllers/login.controller.js
@@ -39,6 +39,7 @@ exports.settings = async function(req,res){
var settings={}
// console.log(inspect(azure))
settings.azureAdEnabled=azure.enable
+ settings.azureGroupfilter=azure.groupfilter
settings.azureGraphUrl=authConfig.azureGraphUrl
res.json(new RestResult("success","",settings,""))
})
diff --git a/server/src/db/create_azuread_table.sql b/server/src/db/create_azuread_table.sql
index b8dab25e..99787607 100644
--- a/server/src/db/create_azuread_table.sql
+++ b/server/src/db/create_azuread_table.sql
@@ -3,6 +3,7 @@ DROP TABLE IF EXISTS `azuread`;
CREATE TABLE `azuread` (
`client_id` text DEFAULT NULL,
`secret_id` text DEFAULT NULL,
- `enable` tinyint(4) DEFAULT NULL
+ `enable` tinyint(4) DEFAULT NULL,
+ `groupfilter` varchar(250) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-INSERT INTO AnsibleForms.azuread(client_id,secret_id,enable) VALUES('','',0);
+INSERT INTO AnsibleForms.azuread(client_id,secret_id,enable,groupfilter) VALUES('','',0,'');
diff --git a/server/src/models/azureAd.model.js b/server/src/models/azureAd.model.js
index a3d3bb12..c80e9800 100644
--- a/server/src/models/azureAd.model.js
+++ b/server/src/models/azureAd.model.js
@@ -10,13 +10,14 @@ var AzureAd=function(azuread){
this.client_id = azuread.client_id;
this.secret_id = encrypt(azuread.secret_id);
this.enable = (azuread.enable)?1:0;
+ this.groupfilter = azuread.groupfilter;
};
AzureAd.update = function (record) {
logger.info(`Updating azuread`)
return mysql.do("UPDATE AnsibleForms.`azuread` set ?", record)
};
AzureAd.isEnabled = function(){
- return mysql.do("SELECT enable FROM AnsibleForms.`azuread` limit 1;")
+ return mysql.do("SELECT enable,groupfilter FROM AnsibleForms.`azuread` limit 1;")
.then((res)=>{
if(res.length>0){
return res[0]
diff --git a/server/src/models/schema.model.js b/server/src/models/schema.model.js
index 2a81e9ed..c2d81455 100644
--- a/server/src/models/schema.model.js
+++ b/server/src/models/schema.model.js
@@ -273,6 +273,7 @@ function patchAll(){
buffer = fs.readFileSync(`${__dirname}/../db/create_azuread_table.sql`)
sql = buffer.toString()
tablePromises.push(addTable("azuread",sql)) // add azuread table
+ tablePromises.push(addColumn("azuread","groupfilter","varchar(250)",true,"NULL")) // add column to limit azuread groups
//tablePromises.push(addRecord("settings",["mail_server","mail_port","mail_secure","mail_username","mail_password","mail_from","url"],["''",25,0,"''","''","''","''"]))
// buffer=fs.readFileSync(`${__dirname}/../db/create_settings_table.sql`)
// sql=buffer.toString();
diff --git a/server/src/swagger.json b/server/src/swagger.json
index e76a106d..bdaf19ed 100644
--- a/server/src/swagger.json
+++ b/server/src/swagger.json
@@ -2,7 +2,7 @@
"swagger": "2.0",
"info": {
"description": "This is the swagger interface for AnsibleForms.\r\nUse the `/auth/login` api with basic authentication to obtain a JWT token.\r\nThen use the access token, prefixed with the word '**Bearer**' to use all other api's.\r\nNote that the access token is limited in time. You can then either login again and get a new set of tokens or use the `/token` api and the refresh token to obtain a new set (preferred).",
- "version": "4.0.18",
+ "version": "4.0.19",
"title": "AnsibleForms",
"contact": {
"email": "info@ansibleforms.com"