UNPKG

copious-transitions

Version:
679 lines (591 loc) 34 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: user_service_class.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: user_service_class.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/** * @namespace CopiousTransitions */ const fs = require('fs') const crypto = require('crypto') const clone = require('clone') const passwordGenerator = require('generate-password'); const ShutdownManager = require('./lib/shutdown-manager') //const shutdown_server_helper_factory = require('http-shutdown') const UserHandling = require('./contractual/user_processing') const MimeHandling = require('./contractual/mime_processing') const TranstionHandling = require('./contractual/transition_processing'); const { EventEmitter } = require('events'); // https://github.com/fastify/fastify let g_password_store = [] const PASSWORD_DEPLETION_MIN = 3 const PASSWORD_BLOCKSIZE = 100 // const g_expected_modules = [ "custom_transitions", "db", "middleware", "authorizer", "validator", "static_assets", "dynamic_assets", "expression", "business", "transition_engine", "web_sockets", "endpoint_server" ] // const g_hex_re = /^[0-9a-fA-F]+$/; // /** * * The class provided in this module provides the main method for the entry point to an application running * a CopiousTransitions web server. * * Here is a typical application. * * ``` const CopiousTransitions = require('copious-transitions') // include this module let [config,debug] = [false,false] // define the variables config = "./user-service-myapp.conf" // set the configuration file name if ( process.argv[2] !== undefined ) { config = process.argv[2]; } if ( process.argv[3] !== undefined ) { // maybe use debugging if ( process.argv[3] === 'debug' ) { debug = true } } let transition_app = new CopiousTransitions(config,debug,__dirname) // Initialize everything according to the configuration. // in this application the local directory of the calling module is the place to look for mod path modules to be loaded. transition_app.on('ready',() => { // when the configuration is done the application is ready to run transition_app.run() // run the application }) ``` * * The constructor loads all the modules that are configured for execution. * Take note that the module are not specified at the top of file where this class is defined. * The modules that this process will use are required to be specified in the configuration file. * * The constuctor first calls `load_parameters` which can reset the module offsets as needed. * In some cases, a module might not be listed in the configuration, for instance. * * The `load_parameters` call also defines a set of global variables and methods that can be used throughout the process * as needed. A restriction of the basic module architecture is that these globals be defined here and nowhere else. * (While there is no way to regulate the management of globals in an application, it is recommended to keep any global definitions * in one place.) * * After the `load_parameters` call is performed, the required modules are loaded. Each module is responsible for returning a * constucted object based on the classes defined with it. * * In the module stack, most of the salient variables that must be configured are set in the intialization methods. * The same is true here, and the constructor defers the all the initializations to the initialization method. * The main initialization method is asynchronous, and so this constructor parsels its call out to a thunk. * * The only modules that are loaded by `requires` at the top of this file are the `contractual` modules. * The `contractual` modules are supposed to be universal enough so as to not require overrides. In some cases, * an application might choose to replace the. Such applications can override the class provided here. The method `initlialize_contractuals` * is set aside for the purpose of overriding these methods. However, it is recommended that the existing methods be kept in tact, * as they define the basic structure for handling requests in applications derived from CopiousTransitions. * * @memberof CopiousTransitions */ class CopiousTransitions extends EventEmitter { // constructor(config,debug,caller_dir) { super() this.debug = debug const conf_obj = load_parameters(config,caller_dir) // configuration parameters to select modules, etc. this.conf_obj = conf_obj this.port = conf_obj.port // this.custom_transitions = require(conf_obj.mod_path.custom_transitions) // SPECIAL NAMED TRANSITIONS (PATHS) this.custom_transitions.initialize(conf_obj) // CONFIGURE this.db = require(conf_obj.mod_path.db) // The database interface. Sets up session store, static store, and other DB pathways this.middleware = require(conf_obj.mod_path.middleware) // This is middleware for Express applications this.authorizer = require(conf_obj.mod_path.authorizer) // Put authorization mechanics here. this.statics = require(conf_obj.mod_path.static_assets) // Special handling for fetching static assets. this.dynamics = require(conf_obj.mod_path.dynamic_assets) // Program spends some time creating the asset. this.validator = require(conf_obj.mod_path.validator) // Custom access to field specification from configuration and the application through DB or inline code this.business = require(conf_obj.mod_path.business) // backend tasks that don't return values, but may send them out to other services.. (some procesing involved) this.transition_engine = require(conf_obj.mod_path.transition_engine) this.web_sockets = require(conf_obj.mod_path.web_sockets) // web sockets - serveral types of application supported -- use app chosen ws interface this.endpoint_server = require(conf_obj.mod_path.endpoint_server) // certain applications will handle transition from the backend // this.app = require(conf_obj.mod_path.expression)(conf_obj,this.db); // exports a function this.session_manager = null // this.max_cache_time = 3600000*2 // once every two hours // this.user_handler = false this.mime_handler = false this.transition_processing = false // this.caller_dir = caller_dir // let b = (async () => { await this.initialize_all(conf_obj) this.setup_paths(conf_obj) this.emit('ready') }) b() } // INITIALIZE /** * * @param {object} conf_obj */ async initialize_all(conf_obj) { // this.db.initialize(conf_obj) this.business.initialize(conf_obj,this.db) this.transition_engine.initialize(conf_obj,this.db) // this.session_manager = this.authorizer.sessions(this.app,this.db,this.business,this.transition_engine) // setup session management, session, cookies, tokens, etc. Use database and Express api. // sessions inializes the custom session manager determined in the application authorizer. this.middleware.setup(this.app,this.db,this.session_manager) // use a module to cusomize the use of Express middleware. this.validator.initialize(conf_obj,this.db,this.session_manager) // The validator may refer to stored items and look at other context dependent information this.statics.initialize(this.db,conf_obj) // Static assets may be taken out of DB storage or from disk, etc. await this.dynamics.initialize(this.db,conf_obj) // Dynamichk assets may be taken out of DB storage or from disk, etc. this.transition_engine.install(this.statics,this.dynamics,this.session_manager) // --- contractual logic --- this.initlialize_contractuals() // websocket access to contractual logic this.web_sockets.initialize(conf_obj,this.app) this.web_sockets.set_contractual_filters(this.transition_processing,this.user_handler,this.mime_handler) // this.endpoint_server.initialize(conf_obj,this.db) this.endpoint_server.set_contractual_filters(this.transition_processing,this.user_handler,this.mime_handler) this.endpoint_server.set_ws(this.web_sockets) // // transition engine access to web sockets and contractual logic this.transition_engine.set_ws(this.web_sockets) this.transition_engine.set_contractual_filters(this.transition_processing,this.user_handler,this.mime_handler) // } /** * Construct the contractual handlers. Simply new object instances. * Pass in the previously allocated handlers. */ initlialize_contractuals() { let use_foreign = false // conf_obj.foreign_auth.allowed (deprecated) this.user_handler = new UserHandling(this.session_manager,this.validator,this.statics,this.dynamics,this.transition_engine,use_foreign,this.max_cache_time) this.mime_handler = new MimeHandling(this.session_manager,this.validator,this.statics,this.dynamics,this.max_cache_time) this.transition_processing = new TranstionHandling(this.session_manager,this.validator,this.dynamics,this.max_cache_time) } /** * Set up the web api paths which will be used by the router to find the handler methods. * The paths specified in this method are a short list of paths describing the general types of transactions * that are needed to carry out actions for fetching something, creating something to be returned as a mime type asset, * or for performing a state transition. Some paths are of the type that are *guarded*. Guarded paths require user identification * via session identification and token matching, especially if there is a secondary action. * * Here is a list of the paths that this module defines: * * * /static/:asset            -- previously static_mime. This just returns a static (not computed) piece of information. The app should be able to set the mime type. * * /guarded/static/:asset    -- previously keyed_mime.   Same as above, but checks the session for access. * * /guarded/dynamic/:asset   -- does the same as static but calls on functions that generate assets -- could be a function service. * * /guarded/secondary        -- a handshake that may be required of some assets before delivery of the asset. * * /transition/:transition   -- a kind of transition of the session -- this may return a dynamic asset or it might just process a form -- a change of state expected * * /transition/secondary     -- a handshake that may be required of some assets before delivery of the asset and always finalization of transition * * '/users/login','/users/logout','/users/register','/users/forgot' -- the start, stop sessions, manage passwords. * * /users/secondary/:action -- the handler for user session management that may require a handshake or finalization. * * Each of these paths hands off processing to a type of contractual class. Each contractual class defines the checks on permission * and availability that is needed to retrieve an asset or perform an action. (Note: contractual class in this case do not have to * do with blockchain contract processing [yet such versions of these classes can be imagined]) What is meant by contractual here * is that the client (browser... usually) and the server will provide authorization, session identity, and token handshakes * in a particular, well-defined way. * * Note that that there are three types of contractual classes: * * 1. user processing - this type of processing has to do with the user session being established or terminated and user existence. * 2. mime processing - This processing has to do with items being returned to the client, e.g. images, text, HTML, JSON, etc. * 3. transition processing - This proessing has to do with driving a state machine in some way. * * In the case of state machine processing, most of the application are just taking in data, sending it somewhere, and updating a database * about the state of the data and/or the user. In some applications, an actual state machine (for the user) might be implemented and the finalization * of the state will result in reseting the state of the machine for a particular user. * * How the methods map to the contractual methods: * * * /static/:asset            -- `mime_handler.static_asset_handler` * * /guarded/static/:asset    -- `mime_handler.guarded_static_asset_handler` * * /guarded/dynamic/:asset   -- `mime_handler.guarded_dynamic_asset_handler` * * /guarded/secondary        -- `mime_handler.guarded_secondary_asset_handler` * * /transition/:transition   -- `transition_processing.transition_handler` * * /transition/secondary     -- `transition_processing.secondary_transition_handler` * * '/users/login','/users/logout','/users/register','/users/forgot' -- user_handler.user_sessions_processing * * /users/secondary/:action -- `transition_processing.secondary_transition_handler` * * @param {object} conf_obj */ setup_paths(conf_obj) { /* /static/:asset            -- previously static_mime. This just returns a static (not computed) piece of information. The app should be able to set the mime type. /guarded/static/:asset    -- previously keyed_mime.   Same as above, but checks the session for access. /guarded/dynamic/:asset   -- does the same as static but calls on functions that generate assets -- could be a function service. /guarded/secondary        -- a handshake that may be required of some assets before delivery of the asset. /transition/:transition   -- a kind of transition of the session -- this may return a dynamic asset or it might just process a form -- a change of state expected /transition/secondary     -- a handshake that may be required of some assets before delivery of the asset and always finalization of transition '/users/login','/users/logout','/users/register','/users/forgot' -- the start, stop sessions, manage passwords. /users/secondary/:action -- the handler for user session management that may require a handshake or finalization. */ // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- // ROOT ... unauthorized entry point -- this is likely to be done by Nginx and will not be needed here. this.app.get('/', (req, res) => { try { let html = this.statics.fetch('index.html'); console.log(html) res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); } catch (e) { res.end('system check') } }); // STATIC FETCH this.app.get('/static/:asset', async (req, res) => { let asset = req.params['asset'] let body = {} let [code,header,data] = await this.mime_handler.static_asset_handler(asset,body,req.headers) if ( data !== false ) { res.writeHead(code,header); res.end(data); } else { res.status(code).send(JSON.stringify(header)) } }); // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- // STATIC KEYED MIME_TYPES -- no form validation, but GUARD asset within a session this.app.post('/guarded/static/:asset', async (req, res) => { let asset = req.params['asset'] let body = req.body let [code,header,data] = await this.mime_handler.guarded_static_asset_handler(asset,body,req.headers) if ( typeof data === 'boolean' ) { res.status(code).send(JSON.stringify(header)) } else { res.writeHead(code,header); res.end(data); } }); this.app.post('/guarded/dynamic/:asset', async (req, res) => { let asset = req.params['asset'] let body = req.body let [code,header,data] = await this.mime_handler.guarded_dynamic_asset_handler(asset,body,req.headers) if ( typeof data === 'boolean' ) { res.status(code).send(JSON.stringify(header)) } else { res.writeHead(code,header); res.end(data); } }); // KEYED MIME_TYPES - TRANSITION ACCEPTED -- the body should send back the token it got with the asset. this.app.post('/secondary/guarded', async (req,res) => { let body = req.body let [code,header,data] = await this.mime_handler.guarded_secondary_asset_handler(body) if ( typeof data === 'boolean' ) { res.status(code).send(JSON.stringify(header)) } else { res.writeHead(code,header); res.end(data); } }) // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- // TRANSITIONS - pure state transition dynamics for sessions this.app.post('/transition/:transition', async (req, res) => { // the transition is a name or key let body = req.body let transition = req.params.transition let [code,data] = await this.transition_processing.transition_handler(transition,body,req.headers) this.app.responder(res).status(code).send(data) }); // TRANSITIONS - TRANSITION ACCEPTED -- the body should send back the token it got with the asset. this.app.post('/secondary/transition',async (req, res) => { let body = req.body // let [code,data] = await this.transition_processing.secondary_transition_handler(body) this.app.responder(res).status(code).send(data) }) // let setup_foreign_auth = false /// conf_obj.foreign_auth.allowed // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- if ( conf_obj.login_app &amp;&amp; Array.isArray(conf_obj.login_app) ) { // LOGIN APPS OPTION (START) // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- /* if ( setup_foreign_auth ) { setup_foreign_auth = (ws) => { this.user_handler.foreign_auth_initializer(ws) } } */ // // USER MANAGEMENT - handle authorization and user presence. for ( let path of conf_obj.login_app ) { // this.app.post(path, async (req,res) => { // let body = req.body let user_op = body['action'] // let [code,result] = await this.user_handler.user_sessions_processing(user_op,body) if ( result.OK === 'true' ) { let transitionObj = result.data this.session_manager.handle_cookies(result,res,transitionObj) } this.app.responder(res).status(code).send(result) // }) } // USER MANAGEMENT - finalize the user action. this.app.post('/secondary/users/:action', async (req, res) => { let body = req.body let action = req.params['action'] let [code,result] = await this.user_handler.secondary_processing(action,body) if ( result.OK === 'true' ) { let transitionObj = result.data this.session_manager.handle_cookies(result,res,transitionObj) } this.app.responder(res).status(code).send(result) }) // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- } // LOGIN APPS OPTION (END) // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- } // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- // // RUN AND STOP // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- /** * The web server will start running. * Before listening, finalize the DB initialization. * Once listening, complete the initialization of the web socket service if it is being used. */ run() { // APPLICATION STARTUP this.db.last_step_initalization() this.app.listen(this.port,() => { console.log(`listening on ${this.port}`) if ( this.web_sockets ) { this.web_sockets.final() } }); } /** * Establish the signal handler that will allow an external process to bring this process down. * The handler will shutdown the DB and wait for the DB shutdown to complete. * In many cases, the DB will write final data to the disk before disconnecting. * Then, use the shutdown manager to clear out any connections and timers. * Finally, exit the process. */ setup_stopping() { process.on('SIGINT', async () => { try { await this.db.disconnect() global_shutdown_manager.shutdown_all() process.exit(0) /* this.shutdown_server_helper.shutdown(async (err) => { process.exit(0) }) */ } catch (e) { console.log(e) process.exit(0) } }); } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- } // ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- /** * */ function generate_password_block() { // var passwords = passwordGenerator.generateMultiple(PASSWORD_BLOCKSIZE, { length: 10, uppercase: false }); // g_password_store = g_password_store.concat(passwords) } /** * * @param {caller_dir|undefined} caller_dir * @returns {string} - the directory from which the mod path modules will be loaded. */ function module_top(caller_dir) { if ( typeof caller_dir === "string" ) { return caller_dir } return process.cwd() } /** * Load the configuration. Called by `load_parameters`. * It is absolutely required that there be a configuration file for any application using this module. * * Pleases refer to documentation referened in the readme.md file for a description of the configuration file. * * @param {string} cpath * @param {boolean} if_module_top * @returns {object} if successful, the configuration object made from reading and parsing the file */ function load_configuration(cpath,if_module_top) { cpath = cpath.trim() try { let conf = fs.readFileSync(cpath,'ascii').toString() return conf } catch (e) { console.log(`Error loading configuration ${cpath}... trying another candidate path`) console.log(e) let cpath2 = '' + cpath while ( cpath2[0] === '.' ) { cpath2 = cpath2.substring(1) } cpath2 = process.cwd() + '/' + cpath2 try { let conf = fs.readFileSync(cpath2,'ascii').toString() return conf } catch (e2) { if ( if_module_top ) { console.log(`Error loading configuration ${cpath2}... trying another candidate path`) let cpath3 = module_top(if_module_top) + '/' + cpath try { let conf = fs.readFileSync(cpath3,'ascii').toString() return conf } catch (e3) { console.log(`Error loading configuration ${cpath3}... failing`) console.log(e) } } } } throw new Error(`could not find the file ${cpath}`) } /** * * @param {object} config * @param {boolean} if_module_top * @returns {object} - the updated configuration object */ function load_parameters(config,if_module_top) { // global.g_debug = false // // // clonify global.clonify = (obj) => { return(clone(obj)) /* if ( typeof obj == "object" ) { return(JSON.parse(JSON.stringify(obj))) } return(obj) */ } // global_appwide_token global.global_appwide_token = () => { // was uuid -- may change return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r &amp; 0x3 | 0x8); return v.toString(16); }); } // isHex global.isHex = (str) => { let check = g_hex_re.test(str) g_hex_re.lastIndex = 0; // be sure to reset the index after using .text() return(check) } // do_hash if ( config.session_token_hasher ) { global.do_hash = require(config.session_token_hasher) config.session_token_hasher = global.do_hash // pass on the override } else { global.do_hash = (text) => { const hash = crypto.createHash('sha256'); hash.update(text); let ehash = hash.digest('base64url'); return(ehash) } } // global_hasher if ( config.global_hasher ) { global.global_hasher = require(config.global_hasher) config.global_hasher = global.global_hasher // pass on the override } else { global.global_hasher = (str,specials) => { // global_hasher(user_str,ipfs) // can ask to store the record on chain... if ( str ) { return do_hash(str) } return('') } } generate_password_block() // generate_password global.generate_password = () => { let password = g_password_store.shift() if ( g_password_store.length &lt; PASSWORD_DEPLETION_MIN ) { setTimeout(generate_password_block,100) } return(password) } global.global_shutdown_manager = new ShutdownManager() try { let data = load_configuration(config,if_module_top) // //console.log(data[p] + " --- " + data.substr(p,40)) // let confJSON = JSON.parse(data) let module_path = confJSON.module_path global.global_module_path = module_path confJSON.mod_path = {} g_expected_modules.forEach(mname => { let modName = confJSON.modules[mname] if ( (typeof modName === 'string') || ( modName === undefined ) ) { if ( modName ) { confJSON.mod_path[mname] = module_top(if_module_top) + `/${module_path}/${modName}` } else { confJSON.mod_path[mname] = __dirname + `/defaults/lib/default_${mname}` } } else if ( typeof modName === 'object' ) { // allow for modules from other locations modName = modName.module let alternate_mod_path = modName.mod_path // perhaps filter this in the future to attain some standard in locations.. confJSON.mod_path[mname] = module_top(if_module_top) + `/${alternate_mod_path}/${modName}` } else { console.log(mname,modName) throw new Error("ill formed module name in config file") } }) return(confJSON) } catch (e) { console.log(e) process.exit(1) } } module.exports = CopiousTransitions </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Namespaces</h3><ul><li><a href="Contractual.html">Contractual</a></li><li><a href="CopiousTransitions.html">CopiousTransitions</a></li><li><a href="DefaultDB.html">DefaultDB</a></li><li><a href="base.html">base</a></li><li><a href="field_validators.html">field_validators</a></li></ul><h3>Classes</h3><ul><li><a href="Contractual.LocalTObjectCache.html">LocalTObjectCache</a></li><li><a href="Contractual.MimeHandling.html">MimeHandling</a></li><li><a href="Contractual.TransitionHandling.html">TransitionHandling</a></li><li><a href="Contractual.UserHandling.html">UserHandling</a></li><li><a href="CopiousTransitions.CopiousTransitions.html">CopiousTransitions</a></li><li><a href="DefaultDB.CustomizationMethodsByApplication.html">CustomizationMethodsByApplication</a></li><li><a href="DefaultDB.FauxInMemStore.html">FauxInMemStore</a></li><li><a href="DefaultDB.FileMapper.html">FileMapper</a></li><li><a href="DefaultDB.FilesAndRelays.html">FilesAndRelays</a></li><li><a href="DefaultDB.FilesAndRelays_base.html">FilesAndRelays_base</a></li><li><a href="DefaultDB.LocalStaticDB.html">LocalStaticDB</a></li><li><a href="DefaultDB.LocalStorageLifeCycle.html">LocalStorageLifeCycle</a></li><li><a href="DefaultDB.LocalStorageSerialization.html">LocalStorageSerialization</a></li><li><a href="DefaultDB.PageableMemStoreElement.html">PageableMemStoreElement</a></li><li><a href="DefaultDB.PersistenceContracts.html">PersistenceContracts</a></li><li><a href="DefaultDB.RemoteMessaging.html">RemoteMessaging</a></li><li><a href="DefaultDB.StaticDBDefault.html">StaticDBDefault</a></li><li><a href="GeneralUserDBWrapperImpl.html">GeneralUserDBWrapperImpl</a></li><li><a href="SessionTokenManager.html">SessionTokenManager</a></li><li><a href="base.DBClass.html">DBClass</a></li><li><a href="base.EndpointManager.html">EndpointManager</a></li><li><a href="base.GeneralAppLifeCycle.html">GeneralAppLifeCycle</a></li><li><a href="base.GeneralAuth.html">GeneralAuth</a></li><li><a href="base.GeneralBusiness.html">GeneralBusiness</a></li><li><a href="base.GeneralDynamic.html">GeneralDynamic</a></li><li><a href="base.GeneralMiddleWare.html">GeneralMiddleWare</a></li><li><a href="base.GeneralStatic.html">GeneralStatic</a></li><li><a href="base.GeneralTransitionEngImpl.html">GeneralTransitionEngImpl</a></li><li><a href="base.SessionManager.html">SessionManager</a></li><li><a href="base.SessionManager_Lite.html">SessionManager_Lite</a></li><li><a href="base.TaggedTransition.html">TaggedTransition</a></li><li><a href="base.TokenTables.html">TokenTables</a></li><li><a href="base.UserMessageEndpoint.html">UserMessageEndpoint</a></li><li><a href="base.WebSocketManager.html">WebSocketManager</a></li><li><a href="field_validators.DataLookupField.html">DataLookupField</a></li><li><a href="field_validators.EmailField.html">EmailField</a></li><li><a href="field_validators.EmailVerifyField.html">EmailVerifyField</a></li><li><a href="field_validators.FieldTest.html">FieldTest</a></li><li><a href="field_validators.FieldValidatorTools.html">FieldValidatorTools</a></li><li><a href="field_validators.ForeignAuth.html">ForeignAuth</a></li><li><a href="field_validators.GeneralValidator.html">GeneralValidator</a></li><li><a href="field_validators.LengthyAlphabetField.html">LengthyAlphabetField</a></li><li><a href="field_validators.LengthyDigitalField.html">LengthyDigitalField</a></li><li><a href="field_validators.LengthyField.html">LengthyField</a></li><li><a href="field_validators.LengthyStringField.html">LengthyStringField</a></li><li><a href="field_validators.PasswordField.html">PasswordField</a></li><li><a href="field_validators.PasswordVerifyField.html">PasswordVerifyField</a></li><li><a href="field_validators.TypeCheckField.html">TypeCheckField</a></li></ul><h3>Global</h3><ul><li><a href="global.html#generate_password_block">generate_password_block</a></li><li><a href="global.html#load_configuration">load_configuration</a></li><li><a href="global.html#load_parameters">load_parameters</a></li><li><a href="global.html#module_top">module_top</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.2</a> on Tue Oct 31 2023 17:32:59 GMT-0700 (Pacific Daylight Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>