From 94bc2fed8b16777394e6e3825aa7eef7b70dda81 Mon Sep 17 00:00:00 2001 From: Mirko Van Colen Date: Tue, 11 Jun 2024 09:47:42 +0200 Subject: [PATCH 01/39] bump to 5.0.3 and fix decode --- CHANGELOG.md | 4 ++++ client/package.json | 2 +- server/package.json | 2 +- server/src/models/job.model.js | 2 +- server/src/swagger.json | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fc75267..a4bb8b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Vault credentials, alpine requires decode -d instead --decode + ## [5.0.2] - 2024-06-10 ### Adding diff --git a/client/package.json b/client/package.json index 4ec63ac4..f61e0c28 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "ansible_forms_vue", - "version": "5.0.2", + "version": "5.0.3", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/server/package.json b/server/package.json index fea12980..b8b11193 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "ansible_forms", - "version": "5.0.2", + "version": "5.0.3", "repository": { "type": "git", "url": "git://github.com/ansibleguy76/ansibleforms.git" diff --git a/server/src/models/job.model.js b/server/src/models/job.model.js index 95fb362e..53d9c271 100644 --- a/server/src/models/job.model.js +++ b/server/src/models/job.model.js @@ -953,7 +953,7 @@ Ansible.launch=async (ev,credentials,jobid,counter,approval,approved=false)=>{ if(!vaultPassword){ command = `ansible-playbook -e '@${extravarsFileName}' -e '@${hiddenExtravarsFileName}'` }else{ - command = `echo ${Buffer.from(vaultPassword).toString('base64')} | base64 --decode | ansible-playbook -e '@${extravarsFileName}' -e '@${hiddenExtravarsFileName}' --vault-password-file=/bin/cat` + command = `echo ${Buffer.from(vaultPassword).toString('base64')} | base64 -d | ansible-playbook -e '@${extravarsFileName}' -e '@${hiddenExtravarsFileName}' --vault-password-file=/bin/cat` } inventory.forEach((item, i) => { command += ` -i '${item}'` }); diff --git a/server/src/swagger.json b/server/src/swagger.json index 660236bb..5237031b 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": "5.0.2", + "version": "5.0.3", "title": "AnsibleForms", "contact": { "email": "info@ansibleforms.com" From 7d1b0072f7e386aae7d96a8bde3043f61809e844 Mon Sep 17 00:00:00 2001 From: Mirko Van Colen Date: Tue, 18 Jun 2024 08:21:55 +0200 Subject: [PATCH 02/39] improved schema check and error messages --- client/src/views/Schema.vue | 11 +++- server/src/controllers/schema.controller.js | 4 +- server/src/init/index.js | 67 +++++++++++++++++---- server/src/models/schema.model.js | 26 +++++--- 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/client/src/views/Schema.vue b/client/src/views/Schema.vue index 19ff0422..3650e16b 100644 --- a/client/src/views/Schema.vue +++ b/client/src/views/Schema.vue @@ -7,7 +7,7 @@
-
+
If this is the first time setup and you don't have your own schema and tables.

Would you like me to try and create the schema and tables ?
@@ -28,6 +28,13 @@
+
+
+ It appears that you have an unuseable schema. Part of the tables is present, and part is missing.
+ Please contact your database or application administrator to either restore from a backup, or create the missing tables. + For now there is nothing I can do for you, until the schema and tables are in a consistent state. +
+
Something went wrong. Most likely the database is simply not reachable. @@ -67,7 +74,7 @@ }, failed(){ if(Array.isArray(this.errorData.error)){ - return this.errorData.error.map((e)=>{return e.message}).join('
') + return this.errorData.error.join('
') }else { return this.errorData.error } diff --git a/server/src/controllers/schema.controller.js b/server/src/controllers/schema.controller.js index 8d2b1809..49fe6077 100644 --- a/server/src/controllers/schema.controller.js +++ b/server/src/controllers/schema.controller.js @@ -1,11 +1,13 @@ 'use strict'; const Schema = require('../models/schema.model'); var RestResult = require('../models/restResult.model'); +var util = require('util'); exports.hasSchema = function(req, res) { Schema.hasSchema() .then((result)=>{ res.json(new RestResult("success","schema and tables are ok",result.data?.success,result.data?.failed)) }) - .catch((result)=>{ + .catch((err)=>{ + var result = err.result // we check if the error has a result if(!result?.data){ res.json(new RestResult("error","FATAL ERROR",[],[result.message])) }else{ diff --git a/server/src/init/index.js b/server/src/init/index.js index d0427c48..cf7474ac 100644 --- a/server/src/init/index.js +++ b/server/src/init/index.js @@ -4,28 +4,69 @@ async function init(){ var Ssh = require('../models/ssh.model'); var Form = require('../models/form.model'); var Job = require('../models/job.model'); + var Schema = require('../models/schema.model'); const mysql=require("../models/db.model"); const Repository = require('../models/repository.model'); const parser = require("cron-parser") const dayjs = require("dayjs") + const util = require('util') - // this is at startup, don't start the app until mysql is ready - // rewrite with await - console.log("Waiting for mysql to start") - async function sleep(millis) { - return new Promise(resolve => setTimeout(resolve, millis)); + // this is at startup, don't start the app until mysql is ready + // rewrite with await + logger.info("Waiting for mysql to start") + async function sleep(millis) { + return new Promise(resolve => setTimeout(resolve, millis)); + } + var MYSQL_IS_READY = false + while(!MYSQL_IS_READY){ + try{ + await mysql.do("SELECT 1") + MYSQL_IS_READY = true + }catch(e){ + logger.log("Mysql not ready yet") + await sleep(5000) } - var MYSQL_IS_READY = false - while(!MYSQL_IS_READY){ - try{ - await mysql.do("SELECT 1") - MYSQL_IS_READY = true - }catch(e){ - console.log("Mysql not ready yet") - await sleep(5000) + } + + logger.info("Mysql is ready") + + // check Schema + try{ + var schemaresult = await Schema.hasSchema() + if(schemaresult.data.failed.length>0){ + logger.warning("Schema is not up to date") + for(let i=0;i0){ + for(let i=0;i{ logger.warning("Failed to generate ssh keys : " + err) diff --git a/server/src/models/schema.model.js b/server/src/models/schema.model.js index ddc1156f..8139de92 100644 --- a/server/src/models/schema.model.js +++ b/server/src/models/schema.model.js @@ -2,6 +2,7 @@ const bcrypt = require('bcrypt'); const logger=require("../lib/logger"); const mysql = require("./db.model") +const Helpers = require("../lib/common") const fs = require('fs'); const NodeCache = require("node-cache") @@ -13,8 +14,7 @@ const cache = new NodeCache({ var Schema=function(){ }; function handleError(err){ - logger.error(err.toString()) - throw(err) + throw err } function checkSchema(){ @@ -31,7 +31,9 @@ function checkSchema(){ }else{ message=`Schema '${db}' is not present` logger.warning(message) - throw({message:message,data:{success:"",failed:message}}) + var err = new Error(message) + err.result = {message:[message],data:{success:[],failed:[message]}} + throw err } } }) @@ -223,7 +225,7 @@ function checkTable(table){ }else{ message=`Table '${table}' is not present` logger.warning(message) - throw({message:message,data:{success:"",failed:message}}) + throw new Error(message) } }) @@ -353,7 +355,7 @@ function checkAll(){ if(x.status === "fulfilled") success.push(x.v) if(x.status === "rejected") - failed.push(x.e) + failed.push(x.e.message) }) messages=messages.concat(success).concat(failed); // overal message if(failed.length==0){ @@ -367,10 +369,14 @@ function checkAll(){ }).catch((res)=>{ messages=messages.concat(res) failed=failed.concat(res) - throw({message:messages,data:{success:"",failed:failed}}) // throw failed + var err = new Error(messages.toString()) + err.result = {message:messages,data:{success:[],failed:failed}} + throw err }) }else { - throw({message:messages,data:{success:success,failed:failed}}) // throw failed + var err = new Error(messages.toString()) + err.result = {message:messages,data:{success:success,failed:failed}} + throw err // throw failed } }, handleError @@ -383,14 +389,18 @@ Schema.create = function () { logger.notice(`Trying to create database schema 'AnsibleForms' and tables`) const buffer=fs.readFileSync(`${__dirname}/../db/create_schema_and_tables.sql`) const query=buffer.toString(); + cache.del('result') // clear cache return mysql.do(query) .then((res)=>{ if(res.length > 0){ logger.notice(`Created schema 'AnsibleForms' and tables`) return `Created schema 'AnsibleForms' and tables` }else{ - throw {message:`Failed to create schema 'AnsibleForms' and/or tables`,data:{success:"",failed:`Failed to create schema 'AnsibleForms' and/or tables`}} + var err = new Error(`Failed to create schema 'AnsibleForms' and/or tables`) + err.result = {message:[`Failed to create schema 'AnsibleForms' and/or tables`],data:{success:[],failed:[`Failed to create schema 'AnsibleForms' and/or tables`]}} + throw err } + }) }; From 5e6dd3e86245fcb6a4ab73bb7e34533da1102b68 Mon Sep 17 00:00:00 2001 From: Mirko Van Colen Date: Tue, 18 Jun 2024 09:06:30 +0200 Subject: [PATCH 03/39] allow disabling schema creation --- client/src/views/Schema.vue | 62 ++++++++++++--------- docs/_data/help.yaml | 16 ++++++ server/config/app.config.js | 1 + server/src/controllers/schema.controller.js | 7 ++- server/src/models/schema.model.js | 37 ++++++------ 5 files changed, 78 insertions(+), 45 deletions(-) diff --git a/client/src/views/Schema.vue b/client/src/views/Schema.vue index 3650e16b..afa5e9c7 100644 --- a/client/src/views/Schema.vue +++ b/client/src/views/Schema.vue @@ -7,34 +7,42 @@
-
+
+ +
+ If this is the first time setup and you don't have your own schema and tables.

+ Would you like me to try and create the schema and tables ?
+ I would create the following :

+ + + + + + + + +
DatabaseschemaAnsibleForms
TablesAll required tables
Usersadmin (pw = AnsibleForms!123)
Groupsadmins
+
+
+ +
+ +
+
+ It appears that you have an unuseable schema. Part of the tables is present, and part is missing.
+ Please contact your database or application administrator to either restore from a backup, or create the missing tables. + For now there is nothing I can do for you, until the schema and tables are in a consistent state. +
+
+
+
- If this is the first time setup and you don't have your own schema and tables.

- Would you like me to try and create the schema and tables ?
- I would create the following :

- - - - - - - - -
DatabaseschemaAnsibleForms
TablesAll required tables
Usersadmin (pw = AnsibleForms!123)
Groupsadmins
-
-
- -
- -
-
- It appears that you have an unuseable schema. Part of the tables is present, and part is missing.
- Please contact your database or application administrator to either restore from a backup, or create the missing tables. - For now there is nothing I can do for you, until the schema and tables are in a consistent state. -
-
+ Schema creation is disabled by your administrator.
+ Please contact your database or application administrator to create the schema and tables. +
+
Something went wrong. Most likely the database is simply not reachable. diff --git a/docs/_data/help.yaml b/docs/_data/help.yaml index 36b53b39..bf4fad31 100644 --- a/docs/_data/help.yaml +++ b/docs/_data/help.yaml @@ -103,6 +103,22 @@ default: https://graph.microsoft.com description: | This variable is used to acquire data from the Azure AD api. + - name: ALLOW_SCHEMA_CREATION + type: number + choices: + - name: 0 + description: Disables schema creation + - name: 1 + description: Enables schema creation + default: 1 + short: Enable the schema creation + description: | + When you start Ansible Forms for the first time and there is no database, Ansible Forms will present you the option to create the schema. + Although this is fairly handy, you might want to disable this option to prevent accidental schema re-creation. + Also note that if you use the docker-compose solution, the database is created as part of the Mysql initialization. + If you use a different database, you might want to enable this option, to at the very least create the schema the very first time. + If you want to disable this option, set this variable to 0. + version: 5.0.3 - name: SHOW_DESIGNER type: number choices: diff --git a/server/config/app.config.js b/server/config/app.config.js index 50b26067..d2c9844c 100644 --- a/server/config/app.config.js +++ b/server/config/app.config.js @@ -5,6 +5,7 @@ var app_config = { baseUrl: process.env.BASE_URL || "/", nodeEnvironment: process.env.NODE_ENV || "production", showDesigner: (process.env.SHOW_DESIGNER ?? 1)==1, + allowSchemaCreation: (process.env.ALLOW_SCHEMA_CREATION ?? 1)==1, formsPath: process.env.FORMS_PATH || path.resolve(__dirname + "/../persistent/forms.yaml"), useYtt: (process.env.USE_YTT ?? 0)==1, lockPath: process.env.LOCK_PATH || path.resolve(__dirname + "/../persistent/ansibleForms.lock"), diff --git a/server/src/controllers/schema.controller.js b/server/src/controllers/schema.controller.js index 49fe6077..ea373462 100644 --- a/server/src/controllers/schema.controller.js +++ b/server/src/controllers/schema.controller.js @@ -1,6 +1,7 @@ 'use strict'; const Schema = require('../models/schema.model'); var RestResult = require('../models/restResult.model'); +var Helpers = require('../lib/common') var util = require('util'); exports.hasSchema = function(req, res) { @@ -18,6 +19,8 @@ exports.hasSchema = function(req, res) { }; exports.create = function(req, res) { Schema.create() - .then((result)=>{ res.json(new RestResult("success",result,null,""))}) - .catch((err)=>{res.json(new RestResult("error",err.toString(),null,null))}) + .then((result)=>{ res.json(new RestResult("success",result,null,"")) }) + .catch((err)=>{ + res.json(new RestResult("error",Helpers.getError(err),null,null)) + }) }; diff --git a/server/src/models/schema.model.js b/server/src/models/schema.model.js index 8139de92..5de7a76c 100644 --- a/server/src/models/schema.model.js +++ b/server/src/models/schema.model.js @@ -3,6 +3,7 @@ const bcrypt = require('bcrypt'); const logger=require("../lib/logger"); const mysql = require("./db.model") const Helpers = require("../lib/common") +const appConfig = require('../../config/app.config') const fs = require('fs'); const NodeCache = require("node-cache") @@ -329,7 +330,7 @@ function checkAll(){ var resultobj=undefined resultobj=cache.get('result') // get from cache, we only check the schema once (or once a day) if(resultobj){ - logger.debug("Schema check from cache") + // logger.debug("Schema check from cache") return Promise.resolve(resultobj) } return checkSchema() // schema @@ -387,21 +388,25 @@ Schema.hasSchema = function(){ } Schema.create = function () { logger.notice(`Trying to create database schema 'AnsibleForms' and tables`) - const buffer=fs.readFileSync(`${__dirname}/../db/create_schema_and_tables.sql`) - const query=buffer.toString(); - cache.del('result') // clear cache - return mysql.do(query) - .then((res)=>{ - if(res.length > 0){ - logger.notice(`Created schema 'AnsibleForms' and tables`) - return `Created schema 'AnsibleForms' and tables` - }else{ - var err = new Error(`Failed to create schema 'AnsibleForms' and/or tables`) - err.result = {message:[`Failed to create schema 'AnsibleForms' and/or tables`],data:{success:[],failed:[`Failed to create schema 'AnsibleForms' and/or tables`]}} - throw err - } - - }) + if(appConfig.allowSchemaCreation){ // added in 5.0.3 + const buffer=fs.readFileSync(`${__dirname}/../db/create_schema_and_tables.sql`) + const query=buffer.toString(); + cache.del('result') // clear cache + return mysql.do(query) + .then((res)=>{ + if(res.length > 0){ + logger.notice(`Created schema 'AnsibleForms' and tables`) + return `Created schema 'AnsibleForms' and tables` + }else{ + var err = new Error(`Failed to create schema 'AnsibleForms' and/or tables`) + throw err + } + + }) + }else{ + var err = new Error(`Schema creation is disabled`) + return Promise.reject(err) + } }; module.exports= Schema; From d8f25f1847e22e378b8358b287633a1de70c0eaa Mon Sep 17 00:00:00 2001 From: Markus Daugs Date: Thu, 20 Jun 2024 13:01:46 +0200 Subject: [PATCH 04/39] express router config in async context does not work properly --- server/src/configure.js | 239 ++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 120 deletions(-) diff --git a/server/src/configure.js b/server/src/configure.js index 148a97ff..ce611858 100644 --- a/server/src/configure.js +++ b/server/src/configure.js @@ -25,6 +25,43 @@ const checkAdminMiddleware = require('./lib/common').checkAdminMiddleware // our personal app settings const appConfig = require('../config/app.config') +const logger = require("./lib/logger"); +const schemaRoutes = require("./routes/schema.routes"); +const queryRoutes = require("./routes/query.routes"); +const expressionRoutes = require("./routes/expression.routes"); +const versionRoutes = require("./routes/version.routes"); +const installRoutes = require("./routes/install.routes"); +const lockRoutes = require("./routes/lock.routes"); +const helpRoutes = require("./routes/help.routes"); +const profileRoutes = require("./routes/profile.routes"); +const loginRoutes = require("./routes/login.routes"); +const tokenRoutes = require("./routes/token.routes"); +const jobRoutes = require("./routes/job.routes"); +const userRoutes = require("./routes/user.routes"); +const groupRoutes = require("./routes/group.routes"); +const ldapRoutes = require("./routes/ldap.routes"); +const azureadRoutes = require("./routes/azuread.routes"); +const oidcRoutes = require("./routes/oidc.routes"); +const settingsRoutes = require("./routes/settings.routes"); +const credentialRoutes = require("./routes/credential.routes"); +const sshRoutes = require("./routes/ssh.routes"); +const awxRoutes = require("./routes/awx.routes"); +const logRoutes = require("./routes/log.routes"); +const repositoryRoutes = require("./routes/repository.routes"); +const knownhostsRoutes = require("./routes/knownhosts.routes"); +const configRoutes = require("./routes/config.routes"); + +// we use 4 authentications/authorization strategies +// - basic : with username and password to get jwt tokens +// - azure-ad-oauth2 : microsoft login +// - oidc : open id connect +// - jwt : to use the jwt tokens +// passport (the auth lib used) is smart, if basic authentication headers are detected +// then the basic authentication strategy kicks and the basic login procedure starts +require('./auth/auth_basic'); +require('./auth/auth_jwt'); +const auth_azuread = require('./auth/auth_azuread'); +const auth_oidc = require('./auth/auth_oidc'); // start the app module.exports = app => { @@ -32,126 +69,88 @@ module.exports = app => { // first time run init // from now on, it's async => we wait for mysql to be ready const init = require('./init/') - init().then(()=>{ - - // passport - app.use(session({ - secret: 'AnsibleForms', - resave: false, - saveUninitialized: true - })); - app.use(passport.initialize()); - app.use(passport.session()); - - // we use 4 authentications/authorization strategies - // - basic : with username and password to get jwt tokens - // - azure-ad-oauth2 : microsoft login - // - oidc : open id connect - // - jwt : to use the jwt tokens - // passport (the auth lib used) is smart, if basic authentication headers are detected - // then the basic authentication strategy kicks and the basic login procedure starts - require('./auth/auth_basic'); - require('./auth/auth_jwt'); - const auth_azuread = require('./auth/auth_azuread'); - auth_azuread.initialize() - - const auth_oidc = require('./auth/auth_oidc'); - auth_oidc.initialize() - - app.use(bodyParser.json({limit: '50mb'})); - app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); - - // import api routes - const awxRoutes = require('./routes/awx.routes') - const jobRoutes = require('./routes/job.routes') - const queryRoutes = require('./routes/query.routes') - const expressionRoutes = require('./routes/expression.routes') - const userRoutes = require('./routes/user.routes') - const groupRoutes = require('./routes/group.routes') - const ldapRoutes = require('./routes/ldap.routes') - const azureadRoutes = require('./routes/azuread.routes') - const oidcRoutes = require('./routes/oidc.routes') - const settingsRoutes = require('./routes/settings.routes') - const credentialRoutes = require('./routes/credential.routes') - const loginRoutes = require('./routes/login.routes') - const schemaRoutes = require('./routes/schema.routes') - const tokenRoutes = require('./routes/token.routes') - const configRoutes = require('./routes/config.routes') - const versionRoutes = require('./routes/version.routes') - const lockRoutes = require('./routes/lock.routes') - const profileRoutes = require('./routes/profile.routes') - const sshRoutes = require('./routes/ssh.routes') - const logRoutes = require('./routes/log.routes') - const knownhostsRoutes = require('./routes/knownhosts.routes') - const helpRoutes = require('./routes/help.routes') - const installRoutes = require('./routes/install.routes') - const repositoryRoutes = require('./routes/repository.routes') - - // mysql2 has a bug that can throw an uncaught exception if the mysql server crashes (not enough mem for example) - // also git commands can chain child processes and cause issues - process.on('uncaughtException', function(err) { - // handle the error safely - console.error("An uncaught exception happened, ignore... ",err) - }) - - // using json web tokens as middleware - // the jwtauthentication strategy from passport (/auth/auth.js) - // is used as a middleware. Every route will check the token for validity - const authobj = passport.authenticate('jwt', { session: false }) - - // api routes for browser only (no cors) - const swaggerOptions = { - customSiteTitle: "Ansibleforms Swagger UI", - customfavIcon: `${appConfig.baseUrl}favicon.svg`, - customCssUrl: `${appConfig.baseUrl}assets/css/swagger.css`, - docExpansion:"none" - } - // change basePath dynamically - swaggerDocument.basePath = `${appConfig.baseUrl}api/v1` - app.use(`${appConfig.baseUrl}api-docs`, swaggerUi.serve, swaggerUi.setup(swaggerDocument,swaggerOptions)); - app.use(`${appConfig.baseUrl}api/v1/schema`, schemaRoutes) - - // api routes for querying - app.use(`${appConfig.baseUrl}api/v1/query`,cors(), authobj, queryRoutes) - app.use(`${appConfig.baseUrl}api/v1/expression`,cors(), authobj, expressionRoutes) - - // api route for version - app.use(`${appConfig.baseUrl}api/v1/version`,cors(), versionRoutes) - app.use(`${appConfig.baseUrl}api/v1/install`,cors(), installRoutes) - - app.use(`${appConfig.baseUrl}api/v1/lock`,cors(),authobj, lockRoutes) - app.use(`${appConfig.baseUrl}api/v1/help`,cors(),authobj, helpRoutes) - - // api route for profile - app.use(`${appConfig.baseUrl}api/v1/profile`,cors(), authobj, profileRoutes) - - // api routes for authorization - app.use(`${appConfig.baseUrl}api/v1/auth`,cors(), loginRoutes) - app.use(`${appConfig.baseUrl}api/v1/token`,cors(), tokenRoutes) - - // api routes for automation actions - - // app.use(`${appConfig.baseUrl}api/v1/multistep`,cors(), authobj, multistepRoutes) - - // api routes for admin management - app.use(`${appConfig.baseUrl}api/v1/job`,cors(), authobj, jobRoutes) - app.use(`${appConfig.baseUrl}api/v1/user`,cors(), authobj, checkAdminMiddleware, userRoutes) - app.use(`${appConfig.baseUrl}api/v1/group`,cors(), authobj, checkAdminMiddleware, groupRoutes) - app.use(`${appConfig.baseUrl}api/v1/ldap`,cors(), authobj, checkAdminMiddleware, ldapRoutes) - app.use(`${appConfig.baseUrl}api/v1/azuread`,cors(), authobj, checkAdminMiddleware, azureadRoutes) - app.use(`${appConfig.baseUrl}api/v1/oidc`,cors(), authobj, checkAdminMiddleware, oidcRoutes) - app.use(`${appConfig.baseUrl}api/v1/settings`,cors(), authobj, checkAdminMiddleware, settingsRoutes) - app.use(`${appConfig.baseUrl}api/v1/credential`,cors(), authobj, checkAdminMiddleware, credentialRoutes) - app.use(`${appConfig.baseUrl}api/v1/sshkey`,cors(), authobj, checkAdminMiddleware, sshRoutes) - app.use(`${appConfig.baseUrl}api/v1/awx`,cors(), authobj, checkAdminMiddleware, awxRoutes) - app.use(`${appConfig.baseUrl}api/v1/log`,cors(), authobj, checkAdminMiddleware, logRoutes) - app.use(`${appConfig.baseUrl}api/v1/repository`,cors(), authobj, checkAdminMiddleware, repositoryRoutes) - app.use(`${appConfig.baseUrl}api/v1/knownhosts`,cors(), authobj, checkAdminMiddleware, knownhostsRoutes) - - // routes for form config (extra middleware in the routes itself) - app.use(`${appConfig.baseUrl}api/v1/config`,cors(), authobj, configRoutes) - + init() + .then(()=> { + auth_azuread.initialize() + auth_oidc.initialize() + }) + .catch( + r => logger.error(r) + ); + + // passport + app.use(session({ + secret: 'AnsibleForms', + resave: false, + saveUninitialized: true + })); + app.use(passport.initialize()); + app.use(passport.session()); + + app.use(bodyParser.json({limit: '50mb'})); + app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); + + // mysql2 has a bug that can throw an uncaught exception if the mysql server crashes (not enough mem for example) + // also git commands can chain child processes and cause issues + process.on('uncaughtException', function(err) { + // handle the error safely + console.error("An uncaught exception happened, ignore... ",err) }) - + // using json web tokens as middleware + // the jwtauthentication strategy from passport (/auth/auth.js) + // is used as a middleware. Every route will check the token for validity + const authobj = passport.authenticate('jwt', { session: false }) + + // api routes for browser only (no cors) + const swaggerOptions = { + customSiteTitle: "Ansibleforms Swagger UI", + customfavIcon: `${appConfig.baseUrl}favicon.svg`, + customCssUrl: `${appConfig.baseUrl}assets/css/swagger.css`, + docExpansion:"none" + } + // change basePath dynamically + swaggerDocument.basePath = `${appConfig.baseUrl}api/v1` + app.use(`${appConfig.baseUrl}api-docs`, swaggerUi.serve, swaggerUi.setup(swaggerDocument,swaggerOptions)); + app.use(`${appConfig.baseUrl}api/v1/schema`, schemaRoutes) + + // api routes for querying + app.use(`${appConfig.baseUrl}api/v1/query`,cors(), authobj, queryRoutes) + app.use(`${appConfig.baseUrl}api/v1/expression`,cors(), authobj, expressionRoutes) + + // api route for version + app.use(`${appConfig.baseUrl}api/v1/version`,cors(), versionRoutes) + app.use(`${appConfig.baseUrl}api/v1/install`,cors(), installRoutes) + + app.use(`${appConfig.baseUrl}api/v1/lock`,cors(),authobj, lockRoutes) + app.use(`${appConfig.baseUrl}api/v1/help`,cors(),authobj, helpRoutes) + + // api route for profile + app.use(`${appConfig.baseUrl}api/v1/profile`,cors(), authobj, profileRoutes) + + // api routes for authorization + app.use(`${appConfig.baseUrl}api/v1/auth`,cors(), loginRoutes) + app.use(`${appConfig.baseUrl}api/v1/token`,cors(), tokenRoutes) + + // api routes for automation actions + + // app.use(`${appConfig.baseUrl}api/v1/multistep`,cors(), authobj, multistepRoutes) + + // api routes for admin management + app.use(`${appConfig.baseUrl}api/v1/job`,cors(), authobj, jobRoutes) + app.use(`${appConfig.baseUrl}api/v1/user`,cors(), authobj, checkAdminMiddleware, userRoutes) + app.use(`${appConfig.baseUrl}api/v1/group`,cors(), authobj, checkAdminMiddleware, groupRoutes) + app.use(`${appConfig.baseUrl}api/v1/ldap`,cors(), authobj, checkAdminMiddleware, ldapRoutes) + app.use(`${appConfig.baseUrl}api/v1/azuread`,cors(), authobj, checkAdminMiddleware, azureadRoutes) + app.use(`${appConfig.baseUrl}api/v1/oidc`,cors(), authobj, checkAdminMiddleware, oidcRoutes) + app.use(`${appConfig.baseUrl}api/v1/settings`,cors(), authobj, checkAdminMiddleware, settingsRoutes) + app.use(`${appConfig.baseUrl}api/v1/credential`,cors(), authobj, checkAdminMiddleware, credentialRoutes) + app.use(`${appConfig.baseUrl}api/v1/sshkey`,cors(), authobj, checkAdminMiddleware, sshRoutes) + app.use(`${appConfig.baseUrl}api/v1/awx`,cors(), authobj, checkAdminMiddleware, awxRoutes) + app.use(`${appConfig.baseUrl}api/v1/log`,cors(), authobj, checkAdminMiddleware, logRoutes) + app.use(`${appConfig.baseUrl}api/v1/repository`,cors(), authobj, checkAdminMiddleware, repositoryRoutes) + app.use(`${appConfig.baseUrl}api/v1/knownhosts`,cors(), authobj, checkAdminMiddleware, knownhostsRoutes) + + // routes for form config (extra middleware in the routes itself) + app.use(`${appConfig.baseUrl}api/v1/config`,cors(), authobj, configRoutes) } From 318004bb3a76970c1330f239ba1f24f562485a0a Mon Sep 17 00:00:00 2001 From: Markus Daugs Date: Thu, 20 Jun 2024 13:36:03 +0200 Subject: [PATCH 05/39] fixed cookie-session handling --- server/src/configure.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/server/src/configure.js b/server/src/configure.js index ce611858..8b55a398 100644 --- a/server/src/configure.js +++ b/server/src/configure.js @@ -87,6 +87,21 @@ module.exports = app => { app.use(passport.initialize()); app.use(passport.session()); + // register regenerate & save after the cookieSession middleware initialization + app.use(function(request, response, next) { + if (request.session && !request.session.regenerate) { + request.session.regenerate = (cb) => { + cb() + } + } + if (request.session && !request.session.save) { + request.session.save = (cb) => { + cb() + } + } + next() + }) + app.use(bodyParser.json({limit: '50mb'})); app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); From 5e8c3a9680fa1b230d0dd86de4d866b48a32a205 Mon Sep 17 00:00:00 2001 From: Markus Daugs Date: Thu, 20 Jun 2024 15:58:53 +0200 Subject: [PATCH 06/39] fixed file loading on import --- server/src/configure.js | 80 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/server/src/configure.js b/server/src/configure.js index 8b55a398..d29bed4c 100644 --- a/server/src/configure.js +++ b/server/src/configure.js @@ -23,49 +23,49 @@ const passport = require('passport'); // a small custom middleware to check whether the user is administrator const checkAdminMiddleware = require('./lib/common').checkAdminMiddleware -// our personal app settings -const appConfig = require('../config/app.config') -const logger = require("./lib/logger"); -const schemaRoutes = require("./routes/schema.routes"); -const queryRoutes = require("./routes/query.routes"); -const expressionRoutes = require("./routes/expression.routes"); -const versionRoutes = require("./routes/version.routes"); -const installRoutes = require("./routes/install.routes"); -const lockRoutes = require("./routes/lock.routes"); -const helpRoutes = require("./routes/help.routes"); -const profileRoutes = require("./routes/profile.routes"); -const loginRoutes = require("./routes/login.routes"); -const tokenRoutes = require("./routes/token.routes"); -const jobRoutes = require("./routes/job.routes"); -const userRoutes = require("./routes/user.routes"); -const groupRoutes = require("./routes/group.routes"); -const ldapRoutes = require("./routes/ldap.routes"); -const azureadRoutes = require("./routes/azuread.routes"); -const oidcRoutes = require("./routes/oidc.routes"); -const settingsRoutes = require("./routes/settings.routes"); -const credentialRoutes = require("./routes/credential.routes"); -const sshRoutes = require("./routes/ssh.routes"); -const awxRoutes = require("./routes/awx.routes"); -const logRoutes = require("./routes/log.routes"); -const repositoryRoutes = require("./routes/repository.routes"); -const knownhostsRoutes = require("./routes/knownhosts.routes"); -const configRoutes = require("./routes/config.routes"); - -// we use 4 authentications/authorization strategies -// - basic : with username and password to get jwt tokens -// - azure-ad-oauth2 : microsoft login -// - oidc : open id connect -// - jwt : to use the jwt tokens -// passport (the auth lib used) is smart, if basic authentication headers are detected -// then the basic authentication strategy kicks and the basic login procedure starts -require('./auth/auth_basic'); -require('./auth/auth_jwt'); -const auth_azuread = require('./auth/auth_azuread'); -const auth_oidc = require('./auth/auth_oidc'); - // start the app module.exports = app => { + // our personal app settings + const appConfig = require('../config/app.config') + const logger = require("./lib/logger"); + const schemaRoutes = require("./routes/schema.routes"); + const queryRoutes = require("./routes/query.routes"); + const expressionRoutes = require("./routes/expression.routes"); + const versionRoutes = require("./routes/version.routes"); + const installRoutes = require("./routes/install.routes"); + const lockRoutes = require("./routes/lock.routes"); + const helpRoutes = require("./routes/help.routes"); + const profileRoutes = require("./routes/profile.routes"); + const loginRoutes = require("./routes/login.routes"); + const tokenRoutes = require("./routes/token.routes"); + const jobRoutes = require("./routes/job.routes"); + const userRoutes = require("./routes/user.routes"); + const groupRoutes = require("./routes/group.routes"); + const ldapRoutes = require("./routes/ldap.routes"); + const azureadRoutes = require("./routes/azuread.routes"); + const oidcRoutes = require("./routes/oidc.routes"); + const settingsRoutes = require("./routes/settings.routes"); + const credentialRoutes = require("./routes/credential.routes"); + const sshRoutes = require("./routes/ssh.routes"); + const awxRoutes = require("./routes/awx.routes"); + const logRoutes = require("./routes/log.routes"); + const repositoryRoutes = require("./routes/repository.routes"); + const knownhostsRoutes = require("./routes/knownhosts.routes"); + const configRoutes = require("./routes/config.routes"); + + // we use 4 authentications/authorization strategies + // - basic : with username and password to get jwt tokens + // - azure-ad-oauth2 : microsoft login + // - oidc : open id connect + // - jwt : to use the jwt tokens + // passport (the auth lib used) is smart, if basic authentication headers are detected + // then the basic authentication strategy kicks and the basic login procedure starts + require('./auth/auth_basic'); + require('./auth/auth_jwt'); + const auth_azuread = require('./auth/auth_azuread'); + const auth_oidc = require('./auth/auth_oidc'); + // first time run init // from now on, it's async => we wait for mysql to be ready const init = require('./init/') From a22cdd425fc01ff4399b7c141310b0196e799264 Mon Sep 17 00:00:00 2001 From: Mirko Van Colen Date: Thu, 20 Jun 2024 16:59:12 +0200 Subject: [PATCH 07/39] add help --- client/src/components/BulmaTextArea.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/components/BulmaTextArea.vue b/client/src/components/BulmaTextArea.vue index 49d13395..b4581110 100644 --- a/client/src/components/BulmaTextArea.vue +++ b/client/src/components/BulmaTextArea.vue @@ -1,6 +1,7 @@