%PDF- %PDF-
Direktori : /proc/thread-self/root/lib/node_modules/pm2/lib/API/pm2-plus/auth-strategies/ |
Current File : //proc/thread-self/root/lib/node_modules/pm2/lib/API/pm2-plus/auth-strategies/CliAuth.js |
'use strict' const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy') const querystring = require('querystring'); const http = require('http') const fs = require('fs') const url = require('url') const exec = require('child_process').exec const tryEach = require('async/tryEach') const path = require('path') const os = require('os') const needle = require('needle') const chalk = require('chalk') const cst = require('../../../../constants.js') const promptly = require('promptly') module.exports = class CliStrategy extends AuthStrategy { // the client will try to call this but we handle this part ourselves retrieveTokens (km, cb) { this.authenticated = false this.callback = cb this.km = km this.BASE_URI = 'https://id.keymetrics.io'; } // so the cli know if we need to tell user to login/register isAuthenticated () { return new Promise((resolve, reject) => { if (this.authenticated) return resolve(true) let tokensPath = cst.PM2_IO_ACCESS_TOKEN fs.readFile(tokensPath, (err, tokens) => { if (err && err.code === 'ENOENT') return resolve(false) if (err) return reject(err) // verify that the token is valid try { tokens = JSON.parse(tokens || '{}') } catch (err) { fs.unlinkSync(tokensPath) return resolve(false) } // if the refresh tokens is here, the user could be automatically authenticated return resolve(typeof tokens.refresh_token === 'string') }) }) } verifyToken (refresh) { return this.km.auth.retrieveToken({ client_id: this.client_id, refresh_token: refresh }) } // called when we are sure the user asked to be logged in _retrieveTokens (optionalCallback) { const km = this.km const cb = this.callback tryEach([ // try to find the token via the environment (next) => { if (!process.env.PM2_IO_TOKEN) { return next(new Error('No token in env')) } this.verifyToken(process.env.PM2_IO_TOKEN) .then((res) => { return next(null, res.data) }).catch(next) }, // try to find it in the file system (next) => { fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => { if (err) return next(err) // verify that the token is valid tokens = JSON.parse(tokens || '{}') if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) { return next(null, tokens) } this.verifyToken(tokens.refresh_token) .then((res) => { return next(null, res.data) }).catch(next) }) }, // otherwise make the whole flow (next) => { return this.authenticate((err, data) => { if (err instanceof Error) return next(err) // verify that the token is valid this.verifyToken(data.refresh_token) .then((res) => { return next(null, res.data) }).catch(next) }) } ], (err, result) => { // if present run the optional callback if (typeof optionalCallback === 'function') { optionalCallback(err, result) } if (result.refresh_token) { this.authenticated = true let file = cst.PM2_IO_ACCESS_TOKEN fs.writeFile(file, JSON.stringify(result), () => { return cb(err, result) }) } else { return cb(err, result) } }) } authenticate (cb) { console.log(`${cst.PM2_IO_MSG} Using non-browser authentication.`) promptly.confirm(`${cst.PM2_IO_MSG} Do you have a pm2.io account? (y/n)`, (err, answer) => { // Either login or register return answer === true ? this.login(cb) : this.register(cb) }) } login (cb) { let retry = () => { promptly.prompt(`${cst.PM2_IO_MSG} Your username or email: `, (err, username) => { if (err) return retry(); promptly.password(`${cst.PM2_IO_MSG} Your password: `, { replace : '*' }, (err, password) => { if (err) return retry(); console.log(`${cst.PM2_IO_MSG} Authenticating ...`) this._loginUser({ username: username, password: password }, (err, data) => { if (err) { console.error(`${cst.PM2_IO_MSG_ERR} Failed to authenticate: ${err.message}`) return retry() } return cb(null, data) }) }) }) } retry() } register (cb) { console.log(`${cst.PM2_IO_MSG} No problem ! We just need few informations to create your account`) var retry = () => { promptly.prompt(`${cst.PM2_IO_MSG} Please choose an username :`, { validator : this._validateUsername, retry : true }, (err, username) => { promptly.prompt(`${cst.PM2_IO_MSG} Please choose an email :`, { validator : this._validateEmail, retry : true },(err, email) => { promptly.password(`${cst.PM2_IO_MSG} Please choose a password :`, { replace : '*' }, (err, password) => { promptly.confirm(`${cst.PM2_IO_MSG} Do you accept the terms and privacy policy (https://pm2.io/legals/terms_conditions.pdf) ? (y/n)`, (err, answer) => { if (err) { console.error(chalk.bold.red(err)); return retry() } else if (answer === false) { console.error(`${cst.PM2_IO_MSG_ERR} You must accept the terms and privacy policy to contiue.`) return retry() } this._registerUser({ email : email, password : password, username : username }, (err, data) => { console.log('\n') if (err) { console.error(`${cst.PM2_IO_MSG_ERR} Unexpect error: ${err.message}`) console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`) return process.exit(1) } return cb(undefined, data) }) }) }) }) }) } retry() } /** * Register function * @param opts.username * @param opts.password * @param opts.email */ _registerUser (opts, cb) { const data = Object.assign(opts, { password_confirmation: opts.password, accept_terms: true }) needle.post(this.BASE_URI + '/api/oauth/register', data, { json: true, headers: { 'X-Register-Provider': 'pm2-register', 'x-client-id': this.client_id } }, function (err, res, body) { if (err) return cb(err) if (body.email && body.email.message) return cb(new Error(body.email.message)) if (body.username && body.username.message) return cb(new Error(body.username.message)) if (!body.access_token) return cb(new Error(body.msg)) return cb(null, { refresh_token : body.refresh_token.token, access_token : body.access_token.token }) }); } _loginUser (user_info, cb) { const URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' + this.client_id + '&redirect_uri=http://localhost:43532'; needle.get(this.BASE_URI + URL_AUTH, (err, res) => { if (err) return cb(err); var cookie = res.cookies; needle.post(this.BASE_URI + '/api/oauth/login', user_info, { cookies : cookie }, (err, resp, body) => { if (err) return cb(err) if (resp.statusCode != 200) return cb('Wrong credentials') var location = resp.headers['x-redirect'] needle.get(this.BASE_URI + location, { cookies : cookie }, (err, res) => { if (err) return cb(err); var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token; needle.post(this.BASE_URI + '/api/oauth/token', { client_id : this.client_id, grant_type : 'refresh_token', refresh_token : refresh_token, scope : 'all' }, (err, res, body) => { if (err) return cb(err) return cb(null, body) }) }) }) }) } _validateEmail (email) { var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (re.test(email) == false) throw new Error('Not an email'); return email; } _validateUsername (value) { if (value.length < 6) { throw new Error('Min length of 6'); } return value; }; deleteTokens (km) { return new Promise((resolve, reject) => { // revoke the refreshToken km.auth.revoke() .then(res => { // remove the token from the filesystem let file = cst.PM2_IO_ACCESS_TOKEN fs.unlinkSync(file) return resolve(res) }).catch(reject) }) } }