2018-04-03 14:16:57 +00:00
import Ember from 'ember' ;
import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends' ;
2018-07-05 18:28:12 +00:00
import { task } from 'ember-concurrency' ;
2018-04-03 14:16:57 +00:00
const BACKENDS = supportedAuthBackends ( ) ;
2018-04-20 20:39:45 +00:00
const { computed , inject , get } = Ember ;
2018-04-17 22:04:34 +00:00
const DEFAULTS = {
token : null ,
username : null ,
password : null ,
2018-07-20 21:48:25 +00:00
customPath : null ,
2018-04-17 22:04:34 +00:00
} ;
export default Ember . Component . extend ( DEFAULTS , {
2018-07-05 18:28:12 +00:00
router : inject . service ( ) ,
2018-04-05 21:36:33 +00:00
auth : inject . service ( ) ,
flashMessages : inject . service ( ) ,
2018-07-05 18:28:12 +00:00
store : inject . service ( ) ,
2018-04-05 21:36:33 +00:00
csp : inject . service ( 'csp-event' ) ,
2018-07-05 18:28:12 +00:00
// set during init and potentially passed in via a query param
selectedAuth : null ,
methods : null ,
cluster : null ,
redirectTo : null ,
2018-08-16 17:48:24 +00:00
namespace : null ,
// internal
oldNamespace : null ,
didReceiveAttrs ( ) {
this . _super ( ... arguments ) ;
let token = this . get ( 'wrappedToken' ) ;
let newMethod = this . get ( 'selectedAuth' ) ;
let oldMethod = this . get ( 'oldSelectedAuth' ) ;
let ns = this . get ( 'namespace' ) ;
let oldNS = this . get ( 'oldNamespace' ) ;
if ( oldNS === null || oldNS !== ns ) {
this . get ( 'fetchMethods' ) . perform ( ) ;
}
this . set ( 'oldNamespace' , ns ) ;
if ( oldMethod && oldMethod !== newMethod ) {
this . resetDefaults ( ) ;
}
this . set ( 'oldSelectedAuth' , newMethod ) ;
if ( token ) {
this . get ( 'unwrapToken' ) . perform ( token ) ;
}
} ,
2018-07-05 18:28:12 +00:00
2018-04-03 14:16:57 +00:00
didRender ( ) {
2018-07-05 18:28:12 +00:00
this . _super ( ... arguments ) ;
2018-04-03 14:16:57 +00:00
// on very narrow viewports the active tab may be overflowed, so we scroll it into view here
2018-07-05 18:28:12 +00:00
let activeEle = this . element . querySelector ( 'li.is-active' ) ;
if ( activeEle ) {
activeEle . scrollIntoView ( ) ;
}
// this is here because we're changing the `with` attr and there's no way to short-circuit rendering,
// so we'll just nav -> get new attrs -> re-render
2018-09-05 19:28:10 +00:00
if (
( this . get ( 'fetchMethods.isIdle' ) && ! this . get ( 'selectedAuth' ) ) ||
( this . get ( 'selectedAuth' ) && ! this . get ( 'selectedAuthBackend' ) )
) {
2018-08-16 17:48:24 +00:00
this . set ( 'selectedAuth' , this . firstMethod ( ) ) ;
this . get ( 'router' ) . replaceWith ( {
2018-07-05 18:28:12 +00:00
queryParams : {
with : this . firstMethod ( ) ,
wrappedToken : this . get ( 'wrappedToken' ) ,
2018-08-16 17:48:24 +00:00
namespace : this . get ( 'namespace' ) ,
2018-07-05 18:28:12 +00:00
} ,
} ) ;
}
} ,
firstMethod ( ) {
let firstMethod = this . get ( 'methodsToShow.firstObject' ) ;
// prefer backends with a path over those with a type
return get ( firstMethod , 'path' ) || get ( firstMethod , 'type' ) ;
2018-04-03 14:16:57 +00:00
} ,
2018-04-17 22:04:34 +00:00
resetDefaults ( ) {
this . setProperties ( DEFAULTS ) ;
} ,
2018-07-05 18:28:12 +00:00
selectedAuthIsPath : computed . match ( 'selectedAuth' , /\/$/ ) ,
selectedAuthBackend : Ember . computed (
2018-08-16 17:48:24 +00:00
'methods' ,
'methods.[]' ,
2018-07-05 18:28:12 +00:00
'selectedAuth' ,
'selectedAuthIsPath' ,
function ( ) {
2018-07-20 21:48:25 +00:00
let methods = this . get ( 'methods' ) ;
let selectedAuth = this . get ( 'selectedAuth' ) ;
2018-07-05 18:28:12 +00:00
let keyIsPath = this . get ( 'selectedAuthIsPath' ) ;
2018-08-16 17:48:24 +00:00
if ( ! methods ) {
return { } ;
}
2018-07-20 21:48:25 +00:00
if ( keyIsPath ) {
return methods . findBy ( 'path' , selectedAuth ) ;
}
2018-08-16 17:48:24 +00:00
return BACKENDS . findBy ( 'type' , selectedAuth ) ;
2018-07-05 18:28:12 +00:00
}
) ,
2018-04-03 14:16:57 +00:00
2018-07-05 18:28:12 +00:00
providerPartialName : computed ( 'selectedAuthBackend' , function ( ) {
let type = this . get ( 'selectedAuthBackend.type' ) || 'token' ;
type = type . toLowerCase ( ) ;
let templateName = Ember . String . dasherize ( type ) ;
return ` partials/auth-form/ ${ templateName } ` ;
2018-04-03 14:16:57 +00:00
} ) ,
2018-04-05 21:36:33 +00:00
hasCSPError : computed . alias ( 'csp.connectionViolations.firstObject' ) ,
cspErrorText : ` This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI. ` ,
2018-07-05 18:28:12 +00:00
allSupportedMethods : computed ( 'methodsToShow' , 'hasMethodsWithPath' , function ( ) {
let hasMethodsWithPath = this . get ( 'hasMethodsWithPath' ) ;
let methodsToShow = this . get ( 'methodsToShow' ) ;
return hasMethodsWithPath ? methodsToShow . concat ( BACKENDS ) : methodsToShow ;
} ) ,
hasMethodsWithPath : computed ( 'methodsToShow' , function ( ) {
return this . get ( 'methodsToShow' ) . isAny ( 'path' ) ;
} ) ,
2018-08-16 17:48:24 +00:00
methodsToShow : computed ( 'methods' , function ( ) {
2018-07-05 18:28:12 +00:00
let methods = this . get ( 'methods' ) || [ ] ;
let shownMethods = methods . filter ( m =>
BACKENDS . find ( b => get ( b , 'type' ) . toLowerCase ( ) === get ( m , 'type' ) . toLowerCase ( ) )
) ;
return shownMethods . length ? shownMethods : BACKENDS ;
} ) ,
unwrapToken : task ( function * ( token ) {
// will be using the token auth method, so set it here
this . set ( 'selectedAuth' , 'token' ) ;
let adapter = this . get ( 'store' ) . adapterFor ( 'tools' ) ;
try {
let response = yield adapter . toolAction ( 'unwrap' , null , { clientToken : token } ) ;
this . set ( 'token' , response . auth . client _token ) ;
this . send ( 'doSubmit' ) ;
} catch ( e ) {
this . set ( 'error' , ` Token unwrap failed: ${ e . errors [ 0 ] } ` ) ;
}
} ) ,
2018-08-16 17:48:24 +00:00
fetchMethods : task ( function * ( ) {
let store = this . get ( 'store' ) ;
this . set ( 'methods' , null ) ;
store . unloadAll ( 'auth-method' ) ;
try {
let methods = yield store . findAll ( 'auth-method' , {
adapterOptions : {
unauthenticated : true ,
} ,
} ) ;
this . set ( 'methods' , methods ) ;
} catch ( e ) {
this . set ( 'error' , ` There was an error fetching auth methods: ${ e . errors [ 0 ] } ` ) ;
}
} ) ,
showLoading : computed . or ( 'fetchMethods.isRunning' , 'unwrapToken.isRunning' ) ,
2018-04-03 14:16:57 +00:00
handleError ( e ) {
this . set ( 'loading' , false ) ;
2018-04-05 21:36:33 +00:00
let errors = e . errors . map ( error => {
if ( error . detail ) {
return error . detail ;
}
return error ;
} ) ;
this . set ( 'error' , ` Authentication failed: ${ errors . join ( '.' ) } ` ) ;
2018-04-03 14:16:57 +00:00
} ,
actions : {
2018-04-17 22:04:34 +00:00
doSubmit ( ) {
let data = { } ;
2018-04-03 14:16:57 +00:00
this . setProperties ( {
loading : true ,
error : null ,
} ) ;
2018-04-17 22:04:34 +00:00
let targetRoute = this . get ( 'redirectTo' ) || 'vault.cluster' ;
2018-07-05 18:28:12 +00:00
let backend = this . get ( 'selectedAuthBackend' ) || { } ;
let backendMeta = BACKENDS . find (
2018-08-16 17:48:24 +00:00
b => ( get ( b , 'type' ) || '' ) . toLowerCase ( ) === ( get ( backend , 'type' ) || '' ) . toLowerCase ( )
2018-07-05 18:28:12 +00:00
) ;
2018-08-16 17:48:24 +00:00
let attributes = get ( backendMeta || { } , 'formAttributes' ) || { } ;
2018-04-17 22:04:34 +00:00
data = Ember . assign ( data , this . getProperties ( ... attributes ) ) ;
2018-07-20 21:48:25 +00:00
if ( this . get ( 'customPath' ) || get ( backend , 'id' ) ) {
data . path = this . get ( 'customPath' ) || get ( backend , 'id' ) ;
2018-04-03 14:16:57 +00:00
}
const clusterId = this . get ( 'cluster.id' ) ;
2018-04-20 20:39:45 +00:00
this . get ( 'auth' ) . authenticate ( { clusterId , backend : get ( backend , 'type' ) , data } ) . then (
2018-08-16 17:48:24 +00:00
( { isRoot , namespace } ) => {
2018-04-03 14:16:57 +00:00
this . set ( 'loading' , false ) ;
2018-08-16 17:48:24 +00:00
const transition = this . get ( 'router' ) . transitionTo ( targetRoute , { queryParams : { namespace } } ) ;
2018-04-03 14:16:57 +00:00
if ( isRoot ) {
transition . followRedirects ( ) . then ( ( ) => {
this . get ( 'flashMessages' ) . warning (
'You have logged in with a root token. As a security precaution, this root token will not be stored by your browser and you will need to re-authenticate after the window is closed or refreshed.'
) ;
} ) ;
}
} ,
( ... errArgs ) => this . handleError ( ... errArgs )
) ;
} ,
} ,
} ) ;