mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 10:13:21 +00:00
(web) addressing config changes - login page p1
This commit is contained in:
parent
652a7d302e
commit
72fb5cdea3
|
@ -15,8 +15,8 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
module.exports.App = require('./app.jsx');
|
||||
module.exports.Login = require('./login.jsx');
|
||||
module.exports.NewUser = require('./newUser.jsx');
|
||||
module.exports.Login = require('./user/login.jsx');
|
||||
module.exports.NewUser = require('./user/newUser.jsx');
|
||||
module.exports.Nodes = require('./nodes/main.jsx');
|
||||
module.exports.Sessions = require('./sessions/main.jsx');
|
||||
module.exports.CurrentSessionHost = require('./currentSession/main.jsx');
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var $ = require('jQuery');
|
||||
var reactor = require('app/reactor');
|
||||
var LinkedStateMixin = require('react-addons-linked-state-mixin');
|
||||
var {actions, getters} = require('app/modules/user');
|
||||
var GoogleAuthInfo = require('./googleAuthLogo');
|
||||
var cfg = require('app/config');
|
||||
var {TeleportLogo} = require('./icons.jsx');
|
||||
var {SECOND_FACTOR_TYPE_HOTP, SECOND_FACTOR_TYPE_OIDC, SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
|
||||
|
||||
var LoginInputForm = React.createClass({
|
||||
|
||||
mixins: [LinkedStateMixin],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
user: '',
|
||||
password: '',
|
||||
token: '',
|
||||
provider: null,
|
||||
secondFactorType: SECOND_FACTOR_TYPE_HOTP
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onLogin(e){
|
||||
e.preventDefault();
|
||||
this.state.secondFactorType = SECOND_FACTOR_TYPE_HOTP;
|
||||
// token field is required for Google Authenticator
|
||||
$('input[name=token]').addClass("required");
|
||||
if (this.isValid()) {
|
||||
this.props.onClick(this.state);
|
||||
}
|
||||
},
|
||||
|
||||
providerLogin: function (provider) {
|
||||
var self = this;
|
||||
return function (e) {
|
||||
e.preventDefault();
|
||||
self.state.secondFactorType = SECOND_FACTOR_TYPE_OIDC;
|
||||
self.state.provider = provider.id;
|
||||
self.props.onClick(self.state);
|
||||
}
|
||||
},
|
||||
|
||||
onLoginWithU2f: function(e) {
|
||||
e.preventDefault();
|
||||
this.state.secondFactorType = SECOND_FACTOR_TYPE_U2F;
|
||||
// token field not required for U2F
|
||||
$('input[name=token]').removeClass("required");
|
||||
if (this.isValid()) {
|
||||
this.props.onClick(this.state);
|
||||
}
|
||||
},
|
||||
|
||||
isValid: function() {
|
||||
var $form = $(this.refs.form);
|
||||
return $form.length === 0 || $form.valid();
|
||||
},
|
||||
|
||||
render() {
|
||||
let {isProcessing, isFailed, message } = this.props.attemp;
|
||||
let providers = cfg.getAuthProviders();
|
||||
let useU2f = !!cfg.getU2fAppId();
|
||||
|
||||
return (
|
||||
<form ref="form" className="grv-login-input-form">
|
||||
<h3> Welcome to Teleport </h3>
|
||||
<div className="">
|
||||
<div className="form-group">
|
||||
<input autoFocus valueLink={this.linkState('user')} className="form-control required" placeholder="User name" name="userName" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<input valueLink={this.linkState('password')} type="password" name="password" className="form-control required" placeholder="Password"/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<input autoComplete="off" valueLink={this.linkState('token')} className="form-control required" name="token" placeholder="Two factor token (Google Authenticator)"/>
|
||||
</div>
|
||||
<button onClick={this.onLogin} disabled={isProcessing} type="submit" className="btn btn-primary block full-width m-b">Login</button>
|
||||
{ useU2f ? <button onClick={this.onLoginWithU2f} disabled={isProcessing} type="submit" className="btn btn-primary block full-width m-b">Login with U2F</button> : null }
|
||||
{ providers.map((provider) => <button onClick={this.providerLogin(provider)} type="submit" className="btn btn-danger block full-width m-b">With {provider.display}</button>) }
|
||||
{ isProcessing && this.state.secondFactorType == SECOND_FACTOR_TYPE_U2F ? (<label className="help-block">Insert your U2F key and press the button on the key</label>) : null }
|
||||
{ isFailed ? (<label className="error">{message}</label>) : null }
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
var Login = React.createClass({
|
||||
|
||||
mixins: [reactor.ReactMixin],
|
||||
|
||||
getDataBindings() {
|
||||
return {
|
||||
attemp: getters.loginAttemp
|
||||
}
|
||||
},
|
||||
|
||||
onClick(inputData){
|
||||
var loc = this.props.location;
|
||||
var redirect = cfg.routes.app;
|
||||
|
||||
if(loc.state && loc.state.redirectTo){
|
||||
redirect = loc.state.redirectTo;
|
||||
}
|
||||
|
||||
actions.login(inputData, redirect);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="grv-login text-center">
|
||||
<TeleportLogo/>
|
||||
<div className="grv-content grv-flex">
|
||||
<div className="grv-flex-column">
|
||||
<LoginInputForm attemp={this.state.attemp} onClick={this.onClick}/>
|
||||
<GoogleAuthInfo/>
|
||||
<div className="grv-login-info">
|
||||
<i className="fa fa-question"></i>
|
||||
<strong>New Account or forgot password?</strong>
|
||||
<div>Ask for assistance from your Company administrator</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Login;
|
|
@ -14,18 +14,20 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
import React from 'react';
|
||||
|
||||
var GoogleAuthInfo = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<div className="grv-google-auth text-left">
|
||||
<div className="grv-icon-google-auth"/>
|
||||
<strong>Google Authenticator</strong>
|
||||
<div>Download <a href="https://support.google.com/accounts/answer/1066447?hl=en">Google Authenticator</a> on your phone to access your two factor token</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
const GoogleAuthInfo = () => {
|
||||
return (
|
||||
<div className="grv-google-auth text-left">
|
||||
<div className="grv-icon-google-auth"/>
|
||||
<strong>Google Authenticator</strong>
|
||||
<div>Download
|
||||
<a href="https://support.google.com/accounts/answer/1066447?hl=en">
|
||||
<span> Google Authenticator </span>
|
||||
</a>
|
||||
on your phone to access your two factor token</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = GoogleAuthInfo;
|
||||
export default GoogleAuthInfo;
|
287
web/src/app/components/user/login.jsx
Normal file
287
web/src/app/components/user/login.jsx
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import $ from 'jQuery';
|
||||
import reactor from 'app/reactor';
|
||||
import {actions, getters} from 'app/modules/user';
|
||||
import GoogleAuthInfo from './googleAuthLogo';
|
||||
import cfg from 'app/config';
|
||||
import { TeleportLogo } from './../icons.jsx';
|
||||
import { SsoBtnList } from './ssoBtnList';
|
||||
import { Auth2faType, AuthType } from 'app/services/enums';
|
||||
|
||||
const Login = React.createClass({
|
||||
|
||||
mixins: [reactor.ReactMixin],
|
||||
|
||||
getDataBindings() {
|
||||
return {
|
||||
attemp: getters.loginAttemp
|
||||
}
|
||||
},
|
||||
|
||||
onLoginWithOidc(providerName){
|
||||
let redirect = this.getRedirectUrl();
|
||||
actions.loginWithOidc(providerName, redirect);
|
||||
},
|
||||
|
||||
onLoginWithU2f(username, password) {
|
||||
let redirect = this.getRedirectUrl();
|
||||
actions.loginWithU2f(username, password, redirect);
|
||||
},
|
||||
|
||||
onLogin(username, password, token) {
|
||||
let redirect = this.getRedirectUrl();
|
||||
actions.login(username, password, token, redirect);
|
||||
},
|
||||
|
||||
getRedirectUrl() {
|
||||
let loc = this.props.location;
|
||||
let redirect = cfg.routes.app;
|
||||
|
||||
if (loc.state && loc.state.redirectTo) {
|
||||
redirect = loc.state.redirectTo;
|
||||
}
|
||||
|
||||
return redirect;
|
||||
},
|
||||
|
||||
render() {
|
||||
let {attemp} = this.state;
|
||||
let provider = cfg.getAuthProvider();
|
||||
let authType = cfg.getAuthType();
|
||||
let auth2faType = cfg.getAuth2faType();
|
||||
|
||||
return (
|
||||
<div className="grv-login text-center">
|
||||
<TeleportLogo/>
|
||||
<div className="grv-content grv-flex">
|
||||
<div className="grv-flex-column">
|
||||
<LoginInputForm
|
||||
authProvider={provider}
|
||||
auth2faType={auth2faType}
|
||||
authType={authType}
|
||||
onLoginWithOidc={this.onLoginWithOidc}
|
||||
onLoginWithU2f={this.onLoginWithU2f}
|
||||
onLogin={this.onLogin}
|
||||
attemp={attemp}
|
||||
/>
|
||||
<LoginFooter auth2faType={auth2faType}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const LoginInputForm = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
user: '',
|
||||
password: '',
|
||||
token: ''
|
||||
}
|
||||
},
|
||||
|
||||
onLogin(e) {
|
||||
e.preventDefault();
|
||||
this.state.secondFactorType = Auth2faType.OTP;
|
||||
if (this.isValid()) {
|
||||
let { user, password, token } = this.state;
|
||||
this.props.onLogin(user, password, token);
|
||||
}
|
||||
},
|
||||
|
||||
onLoginWithU2f(e) {
|
||||
e.preventDefault();
|
||||
if (this.isValid()) {
|
||||
let { user, password } = this.state;
|
||||
this.props.onLoginWithU2f(user, password);
|
||||
}
|
||||
},
|
||||
|
||||
onLoginWithOidc(e) {
|
||||
e.preventDefault();
|
||||
this.props.onLoginWithOidc(this.props.authProvider);
|
||||
},
|
||||
|
||||
isValid() {
|
||||
var $form = $(this.refs.form);
|
||||
return $form.length === 0 || $form.valid();
|
||||
},
|
||||
|
||||
needsCredentials() {
|
||||
return this.props.authType === AuthType.LOCAL || this.needs2fa();
|
||||
},
|
||||
|
||||
needs2fa() {
|
||||
return !!this.props.auth2faType && this.props.auth2faType !== Auth2faType.DISABLED;
|
||||
},
|
||||
|
||||
render2faFields() {
|
||||
if (!this.needs2fa() || this.props.auth2faType !== Auth2faType.OTP) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-group">
|
||||
<input
|
||||
autoComplete="off"
|
||||
value={this.state.token}
|
||||
onChange={e => this.onChangeState('token', e.target.value)}
|
||||
className="form-control required"
|
||||
name="token"
|
||||
placeholder="Two factor token (Google Authenticator)"/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
onChangeState(propName, value) {
|
||||
this.setState({
|
||||
[propName]: value
|
||||
});
|
||||
},
|
||||
|
||||
renderNameAndPassFields() {
|
||||
if (!this.needsCredentials()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="form-group">
|
||||
<input
|
||||
autoFocus
|
||||
value={this.state.user}
|
||||
onChange={e => this.onChangeState('user', e.target.value)}
|
||||
className="form-control required"
|
||||
placeholder="User name"
|
||||
name="userName"/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<input
|
||||
value={this.state.password}
|
||||
onChange={e => this.onChangeState('password', e.target.value)}
|
||||
type="password"
|
||||
name="password"
|
||||
className="form-control required"
|
||||
placeholder="Password"/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
renderLoginBtn() {
|
||||
let { isProcessing } = this.props.attemp;
|
||||
if (!this.needsCredentials()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let $helpBlock = isProcessing && this.props.auth2faType === Auth2faType.UTF ? (
|
||||
<div className="help-block">
|
||||
Insert your U2F key and press the button on the key
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
|
||||
let onClick = this.props.auth2faType === Auth2faType.UTF ?
|
||||
this.onLoginWithU2f : this.onLogin;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={isProcessing}
|
||||
type="submit"
|
||||
className="btn btn-primary block full-width m-b">
|
||||
Login
|
||||
</button>
|
||||
{$helpBlock}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
renderSsoBtns() {
|
||||
let { authType, authProvider, attemp } = this.props;
|
||||
|
||||
if (authType !== AuthType.OIDC) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let ssoProvider = {
|
||||
name: authProvider,
|
||||
displayName: authProvider
|
||||
}
|
||||
|
||||
return (
|
||||
<SsoBtnList
|
||||
prefixText="Login with "
|
||||
isDisabled={attemp.isProcessing}
|
||||
providers={[ssoProvider]}
|
||||
onClick={this.onLoginWithOidc} />
|
||||
)
|
||||
},
|
||||
|
||||
render() {
|
||||
let { isFailed, message } = this.props.attemp;
|
||||
let $error = isFailed ? (
|
||||
<label className="error">{message}</label>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form ref="form" className="grv-login-input-form">
|
||||
<h3> Welcome to Teleport </h3>
|
||||
<div>
|
||||
{this.renderNameAndPassFields()}
|
||||
{this.render2faFields()}
|
||||
{this.renderLoginBtn()}
|
||||
{this.renderSsoBtns()}
|
||||
{$error}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
LoginInputForm.propTypes = {
|
||||
authProvider: React.PropTypes.string,
|
||||
auth2faType: React.PropTypes.string,
|
||||
authType: React.PropTypes.string,
|
||||
onLoginWithOidc: React.PropTypes.func.isRequired,
|
||||
onLoginWithU2f: React.PropTypes.func.isRequired,
|
||||
onLogin: React.PropTypes.func.isRequired,
|
||||
attemp: React.PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
const LoginFooter = ({auth2faType}) => {
|
||||
let $googleHint = auth2faType === Auth2faType.OTP ? <GoogleAuthInfo /> : null;
|
||||
return (
|
||||
<div>
|
||||
{$googleHint}
|
||||
<div className="grv-login-info">
|
||||
<i className="fa fa-question"></i>
|
||||
<strong>New Account or forgot password?</strong>
|
||||
<div>Ask for assistance from your Company administrator</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login;
|
|
@ -20,8 +20,8 @@ var reactor = require('app/reactor');
|
|||
var {actions, getters} = require('app/modules/user');
|
||||
var LinkedStateMixin = require('react-addons-linked-state-mixin');
|
||||
var GoogleAuthInfo = require('./googleAuthLogo');
|
||||
var {ErrorPage, ErrorTypes} = require('./msgPage');
|
||||
var {TeleportLogo} = require('./icons.jsx');
|
||||
var {ErrorPage, ErrorTypes} = require('./../msgPage');
|
||||
var {TeleportLogo} = require('./../icons.jsx');
|
||||
var {SECOND_FACTOR_TYPE_HOTP, SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
|
||||
var cfg = require('app/config');
|
||||
|
63
web/src/app/components/user/ssoBtnList.jsx
Normal file
63
web/src/app/components/user/ssoBtnList.jsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { AuthProviderEnum } from 'app/services/enums';
|
||||
|
||||
const ProviderIcon = ({ type }) => {
|
||||
|
||||
let iconClass = classnames('fa', {
|
||||
'fa-google': type === AuthProviderEnum.GOOGLE,
|
||||
'fa-windows': type === AuthProviderEnum.MS,
|
||||
'fa-github': type === AuthProviderEnum.GITHUB,
|
||||
'fa-bitbucket': type === AuthProviderEnum.BITBUCKET
|
||||
});
|
||||
|
||||
if (iconClass === 'fa') {
|
||||
iconClass = `${iconClass} fa-openid`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="--sso-icon">
|
||||
<span className={iconClass}></span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const getProviderBtnClass = type => {
|
||||
switch (type) {
|
||||
case AuthProviderEnum.BITBUCKET:
|
||||
return 'btn-bitbucket';
|
||||
case AuthProviderEnum.GITHUB:
|
||||
return 'btn-github';
|
||||
case AuthProviderEnum.MS:
|
||||
return 'btn-microsoft';
|
||||
case AuthProviderEnum.GOOGLE:
|
||||
return 'btn-google';
|
||||
default:
|
||||
return 'btn-openid';
|
||||
}
|
||||
}
|
||||
|
||||
const SsoBtnList = ({providers, prefixText, isDisabled, onClick}) => {
|
||||
let $btns = providers.map((item, index) => {
|
||||
let { name, displayName } = item;
|
||||
displayName = displayName || name;
|
||||
let title = `${prefixText} ${displayName}`
|
||||
let providerBtnClass = getProviderBtnClass(name);
|
||||
let btnClass = `btn grv-user-btn-sso full-width ${providerBtnClass}`;
|
||||
return (
|
||||
<button key={index}
|
||||
disabled={isDisabled}
|
||||
className={btnClass}
|
||||
onClick={onClick}>
|
||||
<ProviderIcon type={name}/>
|
||||
<span>{title}</span>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div> {$btns} </div>
|
||||
)
|
||||
}
|
||||
|
||||
export { SsoBtnList }
|
|
@ -115,6 +115,18 @@ let cfg = {
|
|||
return cfg.auth.oidc_connectors;
|
||||
},
|
||||
|
||||
getAuthProvider() {
|
||||
return 'github';
|
||||
},
|
||||
|
||||
getAuthType() {
|
||||
return 'oidc';
|
||||
},
|
||||
|
||||
getAuth2faType() {
|
||||
return 'utf'
|
||||
},
|
||||
|
||||
getU2fAppId(){
|
||||
return cfg.auth.u2f_appid;
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@ var auth = require('app/services/auth');
|
|||
var session = require('app/services/session');
|
||||
var cfg = require('app/config');
|
||||
var api = require('app/services/api');
|
||||
var {SECOND_FACTOR_TYPE_OIDC, SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
|
||||
var { SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
|
||||
|
||||
var actions = {
|
||||
|
||||
|
@ -79,31 +79,33 @@ var actions = {
|
|||
|
||||
},
|
||||
|
||||
login({user, password, token, provider, secondFactorType}, redirect){
|
||||
if(secondFactorType == SECOND_FACTOR_TYPE_OIDC){
|
||||
let fullPath = cfg.getFullUrl(redirect);
|
||||
window.location = cfg.api.getSsoUrl(fullPath, provider);
|
||||
return;
|
||||
}
|
||||
loginWithOidc(provider, redirect) {
|
||||
let fullPath = cfg.getFullUrl(redirect);
|
||||
window.location = cfg.api.getSsoUrl(fullPath, provider);
|
||||
},
|
||||
|
||||
loginWithU2f(user, password, redirect) {
|
||||
let promise = auth.u2fLogin(user, password);
|
||||
this._handleLoginPromise(promise, redirect);
|
||||
},
|
||||
|
||||
login(user, password, token, redirect) {
|
||||
let promise = auth.login(user, password, token);
|
||||
this._handleLoginPromise(promise, redirect);
|
||||
},
|
||||
|
||||
_handleLoginPromise(promise, redirect) {
|
||||
restApiActions.start(TRYING_TO_LOGIN);
|
||||
|
||||
var onSuccess = function(sessionData){
|
||||
restApiActions.success(TRYING_TO_LOGIN);
|
||||
reactor.dispatch(TLPT_RECEIVE_USER, sessionData.user);
|
||||
session.getHistory().push({pathname: redirect});
|
||||
};
|
||||
|
||||
var onFailure = function(err){
|
||||
var msg = err.responseJSON ? err.responseJSON.message : 'Error';
|
||||
restApiActions.fail(TRYING_TO_LOGIN, msg);
|
||||
};
|
||||
|
||||
if(secondFactorType == SECOND_FACTOR_TYPE_U2F){
|
||||
auth.u2fLogin(user, password).done(onSuccess).fail(onFailure);
|
||||
} else {
|
||||
auth.login(user, password, token).done(onSuccess).fail(onFailure);
|
||||
}
|
||||
promise
|
||||
.done(sessionData => {
|
||||
restApiActions.success(TRYING_TO_LOGIN);
|
||||
reactor.dispatch(TLPT_RECEIVE_USER, sessionData.user);
|
||||
session.getHistory().push({pathname: redirect});
|
||||
})
|
||||
.fail(err => {
|
||||
let msg = api.getErrorText(err);
|
||||
restApiActions.fail(TRYING_TO_LOGIN, msg);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var $ = require("jQuery");
|
||||
var session = require('./session');
|
||||
import $ from 'jQuery';
|
||||
import session from './session';
|
||||
|
||||
const api = {
|
||||
|
||||
|
@ -51,7 +51,25 @@ const api = {
|
|||
}
|
||||
|
||||
return $.ajax($.extend({}, defaultCfg, cfg));
|
||||
}
|
||||
},
|
||||
|
||||
getErrorText(err){
|
||||
let msg = 'Unknown error';
|
||||
|
||||
if (err instanceof Error) {
|
||||
return err.message || msg;
|
||||
}
|
||||
|
||||
if(err.responseJSON && err.responseJSON.message){
|
||||
return err.responseJSON.message;
|
||||
}
|
||||
|
||||
if (err.responseJSON && err.responseJSON.error) {
|
||||
return err.responseJSON.error.message || msg;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = api;
|
||||
export default api;
|
||||
|
|
|
@ -14,19 +14,18 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var api = require('./api');
|
||||
var session = require('./session');
|
||||
var cfg = require('app/config');
|
||||
var $ = require('jQuery');
|
||||
var logger = require('app/common/logger').create('services/auth');
|
||||
import api from './api';
|
||||
import session from './session';
|
||||
import cfg from 'app/config';
|
||||
import $ from 'jQuery';
|
||||
import Logger from 'app/common/logger';
|
||||
|
||||
require('u2f-api-polyfill'); // This puts it in window.u2f
|
||||
// This puts it in window.u2f
|
||||
import 'u2f-api-polyfill';
|
||||
|
||||
const logger = Logger.create('services/auth');
|
||||
|
||||
const AUTH_IS_RENEWING = 'GRV_AUTH_IS_RENEWING';
|
||||
const PROVIDER_GOOGLE = 'google';
|
||||
const SECOND_FACTOR_TYPE_HOTP = 'hotp';
|
||||
const SECOND_FACTOR_TYPE_OIDC = 'oidc';
|
||||
const SECOND_FACTOR_TYPE_U2F = 'u2f';
|
||||
|
||||
const CHECK_TOKEN_REFRESH_RATE = 10 * 1000; // 10 sec
|
||||
|
||||
|
@ -225,11 +224,7 @@ const auth = {
|
|||
}
|
||||
return {responseJSON:{message:"U2F Error: " + errorMsg}};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = auth;
|
||||
module.exports.PROVIDER_GOOGLE = PROVIDER_GOOGLE;
|
||||
module.exports.SECOND_FACTOR_TYPE_HOTP = SECOND_FACTOR_TYPE_HOTP;
|
||||
module.exports.SECOND_FACTOR_TYPE_OIDC = SECOND_FACTOR_TYPE_OIDC;
|
||||
module.exports.SECOND_FACTOR_TYPE_U2F = SECOND_FACTOR_TYPE_U2F;
|
||||
export default auth;
|
||||
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
|
||||
EventTypeEnum: {
|
||||
START: 'session.start',
|
||||
END: 'session.end'
|
||||
}
|
||||
},
|
||||
|
||||
AuthType: {
|
||||
LOCAL: 'local',
|
||||
OIDC: 'oidc',
|
||||
LDAP: 'ldap'
|
||||
},
|
||||
|
||||
Auth2faType: {
|
||||
UTF: 'utf',
|
||||
OTP: 'otp',
|
||||
DISABLED: 'off'
|
||||
},
|
||||
|
||||
AuthProviderEnum: {
|
||||
GOOGLE: 'google',
|
||||
MS: 'microsoft',
|
||||
GITHUB: 'github',
|
||||
BITBUCKET: 'bitbucket'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
115
web/src/styles/bootstrap/bootstrap-social.scss
vendored
Normal file
115
web/src/styles/bootstrap/bootstrap-social.scss
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Social Buttons for Bootstrap (4-12-0)
|
||||
*
|
||||
* Copyright 2013-2015 Panayiotis Lipiridis
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* https://github.com/lipis/bootstrap-social
|
||||
*/
|
||||
|
||||
$bs-height-base: ($line-height-computed + $padding-base-vertical * 2);
|
||||
$bs-height-lg: (floor($font-size-large * $line-height-base) + $padding-large-vertical * 2);
|
||||
$bs-height-sm: (floor($font-size-small * 1.5) + $padding-small-vertical * 2);
|
||||
$bs-height-xs: (floor($font-size-small * 1.2) + $padding-small-vertical + 1);
|
||||
|
||||
.btn-social {
|
||||
position: relative;
|
||||
padding-left: ($bs-height-base + $padding-base-horizontal);
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
> :first-child {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: $bs-height-base;
|
||||
line-height: ($bs-height-base + 2);
|
||||
font-size: 1.6em;
|
||||
text-align: center;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.btn-lg {
|
||||
padding-left: ($bs-height-lg + $padding-large-horizontal);
|
||||
> :first-child {
|
||||
line-height: $bs-height-lg;
|
||||
width: $bs-height-lg;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
}
|
||||
&.btn-sm {
|
||||
padding-left: ($bs-height-sm + $padding-small-horizontal);
|
||||
> :first-child {
|
||||
line-height: $bs-height-sm;
|
||||
width: $bs-height-sm;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
}
|
||||
&.btn-xs {
|
||||
padding-left: ($bs-height-xs + $padding-small-horizontal);
|
||||
> :first-child {
|
||||
line-height: $bs-height-xs;
|
||||
width: $bs-height-xs;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-social-icon {
|
||||
@extend .btn-social;
|
||||
height: ($bs-height-base + 2);
|
||||
width: ($bs-height-base + 2);
|
||||
padding: 0;
|
||||
> :first-child {
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 100%!important;
|
||||
}
|
||||
&.btn-lg {
|
||||
height: $bs-height-lg;
|
||||
width: $bs-height-lg;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
&.btn-sm {
|
||||
height: ($bs-height-sm + 2);
|
||||
width: ($bs-height-sm + 2);
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
&.btn-xs {
|
||||
height: ($bs-height-xs + 2);
|
||||
width: ($bs-height-xs + 2);
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin btn-social($color-bg, $color: #fff) {
|
||||
background-color: $color-bg;
|
||||
@include button-variant($color, $color-bg, rgba(0,0,0,.2));
|
||||
}
|
||||
|
||||
|
||||
.btn-adn { @include btn-social(#d87a68); }
|
||||
.btn-bitbucket { @include btn-social(#205081); }
|
||||
.btn-dropbox { @include btn-social(#1087dd); }
|
||||
.btn-facebook { @include btn-social(#3b5998); }
|
||||
.btn-flickr { @include btn-social(#ff0084); }
|
||||
.btn-foursquare { @include btn-social(#f94877); }
|
||||
.btn-github { @include btn-social(#444444); }
|
||||
.btn-google { @include btn-social(#dd4b39); }
|
||||
.btn-instagram { @include btn-social(#3f729b); }
|
||||
.btn-linkedin { @include btn-social(#007bb6); }
|
||||
.btn-microsoft { @include btn-social(#2672ec); }
|
||||
.btn-odnoklassniki { @include btn-social(#f4731c); }
|
||||
.btn-openid { @include btn-social(#f7931e); }
|
||||
.btn-pinterest { @include btn-social(#cb2027); }
|
||||
.btn-reddit { @include btn-social(#eff7ff, #000); }
|
||||
.btn-soundcloud { @include btn-social(#ff5500); }
|
||||
.btn-tumblr { @include btn-social(#2c4762); }
|
||||
.btn-twitter { @include btn-social(#55acee); }
|
||||
.btn-vimeo { @include btn-social(#1ab7ea); }
|
||||
.btn-vk { @include btn-social(#587ea3); }
|
||||
.btn-yahoo { @include btn-social(#720e9e); }
|
|
@ -58,46 +58,24 @@ limitations under the License.
|
|||
}
|
||||
}
|
||||
|
||||
.rotating {
|
||||
animation: rotating-function 4.00s linear infinite;
|
||||
}
|
||||
@-webkit-keyframes rotating-function {
|
||||
from {
|
||||
-webkit-transform: rotate(0deg);
|
||||
.grv-user-btn-sso{
|
||||
&:not(:first-child){
|
||||
margin-top: 15px;
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes rotating-function {
|
||||
from {
|
||||
-moz-transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-moz-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-ms-keyframes rotating-function {
|
||||
from {
|
||||
-ms-transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-ms-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes rotating-function {
|
||||
from {
|
||||
-o-transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-o-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotating-function {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
|
||||
.--sso-icon{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 32px;
|
||||
font-size: 1.6em;
|
||||
text-align: center;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ limitations under the License.
|
|||
@import "~assets/css/toastr-2.1.2.css";
|
||||
@import "~perfect-scrollbar/dist/css/perfect-scrollbar.css";
|
||||
@import "bootstrap/bootstrap";
|
||||
@import "bootstrap/bootstrap-social";
|
||||
@import 'inspinia/style';
|
||||
|
||||
@import "grv-invite";
|
||||
|
|
|
@ -15,12 +15,20 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var baseCfg = require('./webpack.base');
|
||||
var config = require('./webpack.config');
|
||||
|
||||
config.devtool = 'source-map';
|
||||
config.output.filename = '[name].js';
|
||||
config.cache = true;
|
||||
config.module = {
|
||||
var output = Object.assign({}, baseCfg.output, {
|
||||
filename: '[name].js'
|
||||
});
|
||||
|
||||
var cfg = {
|
||||
entry: baseCfg.entry,
|
||||
resolve: baseCfg.resolve,
|
||||
output: output,
|
||||
cache: true,
|
||||
|
||||
devtool: 'source-map',
|
||||
|
||||
module: {
|
||||
loaders: [
|
||||
baseCfg.loaders.fonts,
|
||||
baseCfg.loaders.svg,
|
||||
|
@ -28,13 +36,15 @@ config.module = {
|
|||
baseCfg.loaders.js({withHot: true}),
|
||||
baseCfg.loaders.scss
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
config.plugins = [
|
||||
baseCfg.plugins.devBuild,
|
||||
baseCfg.plugins.hotReplacement,
|
||||
baseCfg.plugins.createIndexHtml,
|
||||
baseCfg.plugins.vendorBundle
|
||||
];
|
||||
plugins: [
|
||||
baseCfg.plugins.devBuild,
|
||||
baseCfg.plugins.hotReplacement,
|
||||
baseCfg.plugins.createIndexHtml,
|
||||
baseCfg.plugins.vendorBundle
|
||||
]
|
||||
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
module.exports = cfg;
|
||||
|
|
Loading…
Reference in a new issue