'use strict' /** __ __ ( )( ) ___ ____ ____ )( )( / __)( ___)( _ \ )(__)( \__ \ )__) ) / (______)(___/(____)(_)\_) By Nike This file is part of EIC implementation of SPARC. * @category MyEic * @subcategory Libraries * @extends User */ class myUser extends app.LoadedClasses.User { authUrl = ''; preferences = {} /** * */ constructor() { super(); } /** * Checks if user belongs to a role * @param {string} role * @returns {boolean} */ hasRole(role) { return(this.roles.indexOf(role)>-1); } /** * * @returns {Array} */ getRoles() { return(app.User.roles); } /** * @async * @returns {string} * */ fetchServices() { let host = new URL(app.config.userLib.apiDiscoveryEndpoint).host let stage = (host.split('.')[1] != 'eismea') ? '.'+host.split('.')[1] : '' return(fetch('/app/assets/json/global/services.json?'+crypto.randomUUID(), { method: 'GET' }) .then(response=>response.text()) .then(response=>JSON.parse(response.replace(/__host__/g, host).replace(/__stage__/g, stage))) ) } /** * Candidate for deprecation * @returns {Promise} */ getApiServices() { return( this.fetchServices() .then(response => { if(response.success) { // Was to much to ask to respect existing code & existing contract, so do the cleanup here let api = {}; for(let entry of response.payload){ api[entry.resource] = entry.actions.reduce( (acc, v)=>{ acc[v.action]=v.availableMethod; return(acc); }, {} ); } // Now override with exceptions from config. (also creates from exceptions) for(let resource in app.config.userLib.apiStageExceptions){ if(!api.hasOwnProperty(resource)) api[resource] = []; for(let action in app.config.userLib.apiStageExceptions[resource]) { api[resource][action] = app.config.userLib.apiStageExceptions[resource][action]; console.warn(`Replacing / adding existing API with exception for resource: ${resource} action: ${action}`) } } app.config.api = api; } } ) ) } /** * * @param {*} callBack */ checkAuthenticated(callBack){ let headers = {}; if(app.config.userLib.authForwardDomain) { let url = new URL(document.location.href); headers = { 'x-requested-path': url.pathname }; } this.identity = { uuid: 'nike', email: 'info@nicsys.eu' }; this.roles = ['admin'] return fetch(app.config.userLib.authEndpoint+'?'+crypto.randomUUID(),{ headers: headers, method: 'GET', credentials: 'include' }) .then(response => response.json()) .then(async resp => { if(resp.success){ this.authenticationDone = true this.isAuthenticated = resp.payload.isAuthenticated; if(resp.payload.isAuthenticated) { if((!this.identity) || (!this.identity.uuid)) { this.logoutUrl = resp.payload.logoutUri; this.parseUserInfo(resp.payload.userInfo); } this.platformRestrictions = resp.payload.platformRestrictions || null if((this.platformRestrictions) && (!this.isVIP())){ this.ShowCurtain() this.stopKeepAlive() return // not triggering callback avoids any further ctrl loading by the router } if(!app.config.api) await this.getApiServices() if(app.config.userLib.keepAliveSeconds && (app.config.userLib.keepAliveSeconds>0)) this.startKeepAlive() callBack(); } else { console.warn('Authorizer said User was not authenticated !'); this.authUrl = resp.payload.authUrl; this.logoutUrl = resp.payload.logoutUri; callBack(); } } else { console.error('Server error calling authorizer checkAuthenticated (success not true)'); this.stopKeepAlive() // Just in case KAL is active, because we arrive here from KAL itself document.location.href = '/eulogin-error.html'; } }) .catch((err) => { console.error('Server error calling authorizer checkAuthenticated (Network error)',err); document.location.href = '/eulogin-error.html'; }); } /** * * @returns {string} */ getMessageBusUserInfo() { return(this.identity.uuid) } /** * * @param {*} objects An array of URI to be checked * @param {*} role * @returns {Promise} */ getBusinessPermissions(objects, role) { let requestedObjects = { resources: objects } let uri = app.config.userLib.resourcePermissionsEndpoint.replace('{id}', this.identity.uuid); // This is just to make use of the whole base model request mechanism (especially for the 401 flow) let fakeModel = new EICModel(null, null) return( fakeModel.request(uri, 'POST', requestedObjects) .then( async serverData => { return(serverData.payload) }) ) } /** * * @param {string} jumpTo */ logout(jumpTo='') { jumpTo = jumpTo ? jumpTo : this.logoutUrl fetch(app.config.userLib.logoutEndpoint+'?'+crypto.randomUUID(),{ method: 'GET', credentials: 'include' }).then((resp) =>{ window.onbeforeunload = null // If user confirmed to logout, not need to have him confirm he's leaving the app ! document.location.href = jumpTo; }); } /** * This is separated, so that upper layer has a chance to use a login button, or avoid redirection loops. */ gotoLogin() { window.onbeforeunload = null // If user asks to relogin, not need to have him confirm he's leaving the app ! document.location.href = this.authUrl; } /** * */ startKeepAlive() { if((!app.config.userLib.keepAliveSeconds) || this.KALtimer) return this.KALtimer = setInterval(this.doKeepAlive.bind(this), 1000*app.config.userLib.keepAliveSeconds) } /** * */ stopKeepAlive() { if(this.KALtimer) clearInterval(this.KALtimer) this.KALtimer = null } /** * */ doKeepAlive() { this.checkAuthenticated(()=>{ // KAL was successfull... do we care ? }) } /** * * @returns {boolean} */ isVIP(){ if(Array.isArray(this.platformRestrictions.allowedRoles)){ let intersect = this.roles.filter(r => (this.platformRestrictions.allowedRoles.includes(r)) ) if(intersect.length>0) return(true) } if((Array.isArray(this.platformRestrictions.allowedUUIDs)) && (this.platformRestrictions.allowedUUIDs.includes(this.identity.uuid))){ return(true) } return(false) } /** * * @todo should be handled by an external view */ ShowCurtain() { let link = document.createElement("link"); link.rel='stylesheet' link.type='text/css' link.href='/app/thirdparty/eicui/eicui-2.0.css' document.head.appendChild(link) let style = document.createElement('style') style.innerHTML = ` .maintenance{ width: 40vw!important; text-align: center; margin: auto!important; } ` document.head.appendChild(style) let content = document.createElement('div') content.innerHTML = `

Maintenance

The site is currently under maintenance.
Please come back later.
` document.body.appendChild(content) } /** * * @param {*} info */ async parseUserInfo(info) { this.identity = { uuid: info.euLoginId, firstname: info.family_name, lastname: info.given_name, email: info.email }; this.roles = info.userRoles || [] } loadPreferences() { if(app.MessageBus) { app.MessageBus.requestWssGwAction('GET', { key: `${this.identity.uuid}:userPrefs`}) .then(settings => { this.preferences = settings.value; }) } } savePreferences() { if(app.MessageBus) { app.MessageBus.requestWssGwAction('SET', { key: `${this.identity.uuid}:userPrefs`, value: this.preferences }) } } getPreference(path) { let value = null; if(app.MessageBus) { let segments = path.split('.'); let pointer = this.preferences; if(pointer) { for(let segment of segments) { if(pointer[segment]) { if(typeof pointer[segment] == 'object') { pointer = pointer[segment]; } else { value = pointer[segment]; } } else { break; } } } } return value; } setPreference(path, value) { if(app.MessageBus) { let segments = path.split('.'); let pointer = this.preferences; for(let i = 0; i < segments.length - 1; i++) { let segment = segments[i]; if(!pointer[segment]) { pointer[segment] = {}; } pointer = pointer[segment]; } pointer[segments[segments.length - 1]] = value; } this.savePreferences(); } } app.registerClass('User', myUser, true); // for Sparc to use app.registerClass('myUser', myUser, true); // Just to avoid double-loading if squeezed