Auth support
- Created StringUtil class with some useful random string methods. - Create UrlUtil class with useful URL manipulation and builder methods. - Cleaned up some unused libraries (cookies, mocks) from index.html - Added LocalStorage dependency. - Added advanced routing to auth module for OAuth response routing. - Added state resolver methods so we can enforce UI states that require certain session states. - Removed AuthProvider resolver and resource, as they're no longer necessary. - Updated header to point to correct routes. - Updated header to correctly represent state. - Added busy template for "pending" activity. This shouldn't actually show up because the javascript will resolve the view logic too quickly, but it's included for the sake of completion. - Added error state in case we get an error response from the server. It's very basic. - Added request interceptor that attaches an access token to every request if a valid access token exists. - Added OpenId service to handle our redirection and token resolution. - Added Deauthorization (logout) controller. - Added session management controller. - Added search param provider to inject non-hashbang query parameters. Change-Id: Id9b1e7fe9ed98ad4be0a80f1acd4a9e125ec57c9
This commit is contained in:
parent
a91c4e7d4d
commit
9e9ee48918
@ -6,11 +6,11 @@
|
|||||||
"font-awesome": "4.0",
|
"font-awesome": "4.0",
|
||||||
"angular": "1.2.13",
|
"angular": "1.2.13",
|
||||||
"angular-resource": "1.2.13",
|
"angular-resource": "1.2.13",
|
||||||
"angular-cookies": "1.2.13",
|
|
||||||
"angular-sanitize": "1.2.13",
|
"angular-sanitize": "1.2.13",
|
||||||
"bootstrap": "3.1.0",
|
"bootstrap": "3.1.0",
|
||||||
"angular-ui-router": "0.2.8-bowratic-tedium",
|
"angular-ui-router": "0.2.8-bowratic-tedium",
|
||||||
"angular-bootstrap": "0.10.0"
|
"angular-bootstrap": "0.10.0",
|
||||||
|
"angular-local-storage": "0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"angular-mocks": "1.2.13",
|
"angular-mocks": "1.2.13",
|
||||||
|
@ -35,7 +35,10 @@ module.exports = function (config) {
|
|||||||
],
|
],
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
'./dist/js/*.js',
|
'./dist/js/libs.js',
|
||||||
|
'./bower_components/angular-mocks/angular-mocks.js',
|
||||||
|
'./dist/js/storyboard.js',
|
||||||
|
'./dist/js/templates.js',
|
||||||
'./test/unit/**/*.js'
|
'./test/unit/**/*.js'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -35,7 +35,10 @@ module.exports = function (config) {
|
|||||||
],
|
],
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
'./dist/js/*.js',
|
'./dist/js/libs.js',
|
||||||
|
'./bower_components/angular-mocks/angular-mocks.js',
|
||||||
|
'./dist/js/storyboard.js',
|
||||||
|
'./dist/js/templates.js',
|
||||||
'./test/unit/**/*.js'
|
'./test/unit/**/*.js'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
40
src/app/auth/controller/auth_authorize_controller.js
Normal file
40
src/app/auth/controller/auth_authorize_controller.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This controller is responsible for getting an authorization code
|
||||||
|
* having a state and an openid.
|
||||||
|
*
|
||||||
|
* @author Nikita Konovalov
|
||||||
|
*/
|
||||||
|
|
||||||
|
angular.module('sb.auth').controller('AuthAuthorizeController',
|
||||||
|
function ($stateParams, $state, $log, OpenId) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// First, check for the edge case where the API returns an error code
|
||||||
|
// back to us. This should only happen when it fails to properly parse
|
||||||
|
// our redirect_uri and thus just sends the error back to referrer, but
|
||||||
|
// we should still catch it.
|
||||||
|
if (!!$stateParams.error) {
|
||||||
|
$log.debug('Error received, redirecting to auth.error.');
|
||||||
|
$state.go('auth.error', $stateParams);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not an error, let's fire the authorization.
|
||||||
|
OpenId.authorize();
|
||||||
|
});
|
@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This controller handles the logic for the authorization provider list page.
|
* This controller deauthorizes the session and destroys all tokens.
|
||||||
*
|
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
*/
|
||||||
angular.module('sb.auth').controller('AuthLoginController',
|
|
||||||
function ($scope, authProvider) {
|
angular.module('sb.auth').controller('AuthDeauthorizeController',
|
||||||
|
function (Session, $state, $log) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
$scope.authProvider = authProvider;
|
$log.debug('Logging out');
|
||||||
|
Session.destroySession();
|
||||||
|
$state.go('index');
|
||||||
});
|
});
|
@ -15,19 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This resource exposes authorization providers to our angularjs environment,
|
* View controller for authorization error conditions.
|
||||||
* allowing us to manage & control them. It's also used during the
|
|
||||||
* authorization/login process to determine how we're going to allow users to
|
|
||||||
* log in to storyboard.
|
|
||||||
*
|
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
*/
|
||||||
|
angular.module('sb.auth').controller('AuthErrorController',
|
||||||
angular.module('sb.services').factory('AuthProvider',
|
function ($scope, $stateParams) {
|
||||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
return $resource(storyboardApiBase + '/auth/provider/:id',
|
console.warn('AuthErrorController');
|
||||||
{id: '@id'},
|
|
||||||
storyboardApiSignature);
|
|
||||||
|
$scope.error = $stateParams.error || 'Unknown';
|
||||||
|
$scope.errorDescription = $stateParams.error_description ||
|
||||||
|
'No description received from server.';
|
||||||
});
|
});
|
53
src/app/auth/controller/auth_token_controller.js
Normal file
53
src/app/auth/controller/auth_token_controller.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Mirantis 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This controller is responsible for getting an access_token and
|
||||||
|
* a refresh token having an authorization_code.
|
||||||
|
*
|
||||||
|
* @author Nikita Konovalov
|
||||||
|
*/
|
||||||
|
|
||||||
|
angular.module('sb.auth').controller('AuthTokenController',
|
||||||
|
function ($state, $log, OpenId, Session, $searchParams) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// First, check for the edge case where the API returns an error code
|
||||||
|
// back to us. This should only happen when it fails to properly parse
|
||||||
|
// our redirect_uri and thus just sends the error back to referrer, but
|
||||||
|
// we should still catch it.
|
||||||
|
if (!!$searchParams.error) {
|
||||||
|
$log.debug('Error received, redirecting to auth.error.');
|
||||||
|
$state.go('auth.error', $searchParams);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks like there's no error, so let's see if we can resolve a token.
|
||||||
|
// TODO: Finish implementing.
|
||||||
|
OpenId.token($searchParams)
|
||||||
|
.then(
|
||||||
|
function (token) {
|
||||||
|
Session.updateSession(token)
|
||||||
|
.then(function () {
|
||||||
|
$state.go('index');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
Session.destroySession();
|
||||||
|
$state.go('auth.error', error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
44
src/app/auth/http/http_authorization_header.js
Normal file
44
src/app/auth/http/http_authorization_header.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An HTTP request interceptor that attaches an authorization to every HTTP
|
||||||
|
* request, assuming it exists and isn't expired.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').factory('httpAuthorizationHeader',
|
||||||
|
function (AccessToken) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return {
|
||||||
|
request: function (request) {
|
||||||
|
|
||||||
|
// TODO(krotscheck): Only apply the token to requests to
|
||||||
|
// storyboardApiBase.
|
||||||
|
var token = AccessToken.getAccessToken();
|
||||||
|
var type = AccessToken.getTokenType();
|
||||||
|
if (!!token && !AccessToken.isExpired()) {
|
||||||
|
request.headers.Authorization = type + ' ' + token;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
// Attach the HTTP interceptor.
|
||||||
|
.config(function ($httpProvider) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
$httpProvider.interceptors.push('httpAuthorizationHeader');
|
||||||
|
});
|
@ -15,48 +15,48 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Storyboard module contains our adaptive authentication and authorization
|
* This Storyboard module contains our authentication and authorization logic.
|
||||||
* logic.
|
|
||||||
*
|
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
*/
|
||||||
angular.module('sb.auth',
|
angular.module('sb.auth', [ 'sb.services', 'sb.templates', 'ui.router',
|
||||||
[ 'sb.services', 'sb.templates', 'ui.router']
|
'sb.util', 'LocalStorageModule']
|
||||||
)
|
)
|
||||||
.config(function ($stateProvider, $urlRouterProvider,
|
.config(function ($stateProvider, SessionResolver) {
|
||||||
AuthProviderResolver) {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Default rerouting.
|
|
||||||
$urlRouterProvider.when('/auth', '/auth/provider/list');
|
|
||||||
$urlRouterProvider.when('/auth/provider', '/auth/provider/list');
|
|
||||||
|
|
||||||
// Declare the states for this module.
|
// Declare the states for this module.
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('auth', {
|
.state('auth', {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
url: '/auth',
|
template: '<div ui-view></div>',
|
||||||
template: '<div ui-view></div>'
|
url: '/auth'
|
||||||
})
|
})
|
||||||
.state('auth.provider', {
|
.state('auth.authorize', {
|
||||||
abstract: true,
|
url: '/authorize?error&error_description',
|
||||||
url: '/provider',
|
templateUrl: 'app/templates/auth/busy.html',
|
||||||
template: '<div ui-view></div>'
|
controller: 'AuthAuthorizeController',
|
||||||
})
|
|
||||||
.state('auth.provider.list', {
|
|
||||||
url: '/list',
|
|
||||||
templateUrl: 'app/templates/auth/provider/list.html',
|
|
||||||
controller: 'AuthListController',
|
|
||||||
resolve: {
|
resolve: {
|
||||||
authProviders: AuthProviderResolver.resolveAuthProviders
|
isLoggedOut: SessionResolver.requireLoggedOut
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('auth.provider.id', {
|
.state('auth.deauthorize', {
|
||||||
url: '/:id',
|
url: '/deauthorize',
|
||||||
templateUrl: 'app/templates/auth/provider/login.html',
|
templateUrl: 'app/templates/auth/busy.html',
|
||||||
controller: 'AuthLoginController',
|
controller: 'AuthDeauthorizeController',
|
||||||
resolve: {
|
resolve: {
|
||||||
authProvider: AuthProviderResolver.resolveAuthProvider('id')
|
isLoggedIn: SessionResolver.requireLoggedIn
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.state('auth.token', {
|
||||||
|
url: '/token?code&state&error&error_description',
|
||||||
|
templateUrl: 'app/templates/auth/busy.html',
|
||||||
|
controller: 'AuthTokenController',
|
||||||
|
resolve: {
|
||||||
|
isLoggedOut: SessionResolver.requireLoggedOut
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.state('auth.error', {
|
||||||
|
url: '/error?error&error_description',
|
||||||
|
templateUrl: 'app/templates/auth/error.html',
|
||||||
|
controller: 'AuthErrorController'
|
||||||
});
|
});
|
||||||
});
|
});
|
39
src/app/auth/provider/session_state.js
Normal file
39
src/app/auth/provider/session_state.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of constants used by the session service to maintain the user's
|
||||||
|
* current authentication state.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').value('SessionState', {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session state constant, used to indicate that the user is logged in.
|
||||||
|
*/
|
||||||
|
LOGGED_IN: 'logged_in',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session state constant, used to indicate that the user is logged out.
|
||||||
|
*/
|
||||||
|
LOGGED_OUT: 'logged_out',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session state constant, used during initialization when we're not quite
|
||||||
|
* certain yet whether we're logged in or logged out.
|
||||||
|
*/
|
||||||
|
PENDING: 'pending'
|
||||||
|
|
||||||
|
});
|
85
src/app/auth/resolver/session_resolver.js
Normal file
85
src/app/auth/resolver/session_resolver.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of utility methods that may be used during state declaration to enforce
|
||||||
|
* session state. They return asynchronous promises which will either resolve
|
||||||
|
* or reject the state change, depending on what you're asking for.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').constant('SessionResolver',
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the promise based on the current session state. We can't
|
||||||
|
* inject here, since the injector's not ready yet.
|
||||||
|
*/
|
||||||
|
function resolveSessionState(deferred, desiredSessionState, Session) {
|
||||||
|
return function () {
|
||||||
|
var sessionState = Session.getSessionState();
|
||||||
|
if (sessionState === desiredSessionState) {
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
} else {
|
||||||
|
deferred.reject(sessionState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* This resolver asserts that the user is logged
|
||||||
|
* out before allowing a route. Otherwise it fails.
|
||||||
|
*/
|
||||||
|
requireLoggedOut: function ($q, $log, Session, SessionState) {
|
||||||
|
|
||||||
|
$log.debug('Resolving logged-out-only route...');
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var resolveLoggedOut = resolveSessionState(deferred,
|
||||||
|
SessionState.LOGGED_OUT, Session);
|
||||||
|
|
||||||
|
// Do we have to wait for state resolution?
|
||||||
|
if (Session.getSessionState() === SessionState.PENDING) {
|
||||||
|
Session.resolveSessionState().then(resolveLoggedOut);
|
||||||
|
} else {
|
||||||
|
resolveLoggedOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This resolver asserts that the user is logged
|
||||||
|
* in before allowing a route. Otherwise it fails.
|
||||||
|
*/
|
||||||
|
requireLoggedIn: function ($q, $log, Session, $rootScope,
|
||||||
|
SessionState) {
|
||||||
|
|
||||||
|
$log.debug('Resolving logged-in-only route...');
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var resolveLoggedIn = resolveSessionState(deferred,
|
||||||
|
SessionState.LOGGED_IN, Session);
|
||||||
|
|
||||||
|
// Do we have to wait for state resolution?
|
||||||
|
if (Session.getSessionState() === SessionState.PENDING) {
|
||||||
|
Session.resolveSessionState().then(resolveLoggedIn);
|
||||||
|
} else {
|
||||||
|
resolveLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})());
|
160
src/app/auth/service/access_token.js
Normal file
160
src/app/auth/service/access_token.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccessToken storage service, an abstraction layer between our token storage
|
||||||
|
* and the rest of the system. This feature uses localStorage, which means that
|
||||||
|
* our application will NOT support IE7. Once that becomes a requirement, we'll
|
||||||
|
* have to use this abstraction layer to store data in a cookie instead.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').factory('AccessToken',
|
||||||
|
function (localStorageService) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our local storage key name constants
|
||||||
|
*/
|
||||||
|
var TOKEN_TYPE = 'token_type';
|
||||||
|
var ACCESS_TOKEN = 'access_token';
|
||||||
|
var REFRESH_TOKEN = 'refresh_token';
|
||||||
|
var ID_TOKEN = 'id_token';
|
||||||
|
var EXPIRES_IN = 'expires_in';
|
||||||
|
var ISSUE_DATE = 'issue_date';
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the token
|
||||||
|
*/
|
||||||
|
clear: function () {
|
||||||
|
localStorageService.remove(TOKEN_TYPE);
|
||||||
|
localStorageService.remove(ACCESS_TOKEN);
|
||||||
|
localStorageService.remove(REFRESH_TOKEN);
|
||||||
|
localStorageService.remove(ID_TOKEN);
|
||||||
|
localStorageService.remove(EXPIRES_IN);
|
||||||
|
localStorageService.remove(ISSUE_DATE);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all token properties at once.
|
||||||
|
*/
|
||||||
|
setToken: function (jsonToken) {
|
||||||
|
this.setTokenType(jsonToken.token_type);
|
||||||
|
this.setAccessToken(jsonToken.access_token);
|
||||||
|
this.setRefreshToken(jsonToken.refresh_token);
|
||||||
|
this.setIdToken(jsonToken.id_token);
|
||||||
|
this.setIssueDate(jsonToken.issue_date);
|
||||||
|
this.setExpiresIn(jsonToken.expires_in);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current access token expired?
|
||||||
|
*/
|
||||||
|
isExpired: function () {
|
||||||
|
var expiresIn = this.getExpiresIn() || 0;
|
||||||
|
var issueDate = this.getIssueDate() || 0;
|
||||||
|
var now = Math.round((new Date()).getTime() / 1000);
|
||||||
|
|
||||||
|
return issueDate + expiresIn < now;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token type. Bearer, etc.
|
||||||
|
*/
|
||||||
|
getTokenType: function () {
|
||||||
|
return localStorageService.get(TOKEN_TYPE);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the token type.
|
||||||
|
*/
|
||||||
|
setTokenType: function (value) {
|
||||||
|
return localStorageService.set(TOKEN_TYPE, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the date this token was issued.
|
||||||
|
*/
|
||||||
|
getIssueDate: function () {
|
||||||
|
return localStorageService.get(ISSUE_DATE) || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the issue date for the current access token.
|
||||||
|
*/
|
||||||
|
setIssueDate: function (value) {
|
||||||
|
return localStorageService.set(ISSUE_DATE, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of seconds after the issue date when this token
|
||||||
|
* is considered expired.
|
||||||
|
*/
|
||||||
|
getExpiresIn: function () {
|
||||||
|
return localStorageService.get(EXPIRES_IN) || 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the number of seconds from the issue date when this token
|
||||||
|
* will expire.
|
||||||
|
*/
|
||||||
|
setExpiresIn: function (value) {
|
||||||
|
return localStorageService.set(EXPIRES_IN, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the access token.
|
||||||
|
*/
|
||||||
|
getAccessToken: function () {
|
||||||
|
return localStorageService.get(ACCESS_TOKEN) || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the access token.
|
||||||
|
*/
|
||||||
|
setAccessToken: function (value) {
|
||||||
|
return localStorageService.set(ACCESS_TOKEN, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the refresh token.
|
||||||
|
*/
|
||||||
|
getRefreshToken: function () {
|
||||||
|
return localStorageService.get(REFRESH_TOKEN) || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the refresh token.
|
||||||
|
*/
|
||||||
|
setRefreshToken: function (value) {
|
||||||
|
return localStorageService.set(REFRESH_TOKEN, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the id token.
|
||||||
|
*/
|
||||||
|
getIdToken: function () {
|
||||||
|
return localStorageService.get(ID_TOKEN) || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the id token.
|
||||||
|
*/
|
||||||
|
setIdToken: function (value) {
|
||||||
|
return localStorageService.set(ID_TOKEN, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
59
src/app/auth/service/current_user.js
Normal file
59
src/app/auth/service/current_user.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current user service. It pays attention to changes in the application's
|
||||||
|
* session state, and loads the user found in the AccessToken when a valid
|
||||||
|
* session is detected.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').factory('CurrentUser',
|
||||||
|
function (SessionState, Session, AccessToken, $rootScope, $log, $q, User) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current user
|
||||||
|
*/
|
||||||
|
var currentUser = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the current user, if such exists.
|
||||||
|
*/
|
||||||
|
function loadCurrentUser() {
|
||||||
|
if (Session.getSessionState() === SessionState.LOGGED_IN) {
|
||||||
|
var userId = AccessToken.getIdToken();
|
||||||
|
|
||||||
|
$log.debug('Loading Current User ' + userId);
|
||||||
|
currentUser = User.get({id: userId});
|
||||||
|
} else {
|
||||||
|
currentUser = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootScope.$on(SessionState.LOGGED_IN, loadCurrentUser);
|
||||||
|
$rootScope.$on(SessionState.LOGGED_OUT, loadCurrentUser);
|
||||||
|
|
||||||
|
loadCurrentUser();
|
||||||
|
|
||||||
|
// Expose the methods for this service.
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Retrieve the current user.
|
||||||
|
*/
|
||||||
|
get: function () {
|
||||||
|
return currentUser;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
106
src/app/auth/service/open_id.js
Normal file
106
src/app/auth/service/open_id.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our OpenID token resource, which adheres to the OpenID connect specification
|
||||||
|
* found here; http://openid.net/specs/openid-connect-basic-1_0.html
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').factory('OpenId',
|
||||||
|
function ($location, $window, $log, $http, $q, StringUtil, UrlUtil,
|
||||||
|
storyboardApiBase, localStorageService) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var storageKey = 'openid_authorize_state';
|
||||||
|
var authorizeUrl = storyboardApiBase + '/openid/authorize';
|
||||||
|
var tokenUrl = storyboardApiBase + '/openid/token';
|
||||||
|
var redirectUri = UrlUtil.buildApplicationUrl('/auth/token');
|
||||||
|
var clientId = $location.host();
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Asks the OAuth endpoint for an authorization token given
|
||||||
|
* the passed parameters.
|
||||||
|
*/
|
||||||
|
authorize: function () {
|
||||||
|
// Create and store a random state parameter.
|
||||||
|
var state = StringUtil.randomAlphaNumeric(20);
|
||||||
|
localStorageService.set(storageKey, state);
|
||||||
|
|
||||||
|
var openIdParams = {
|
||||||
|
response_type: 'code',
|
||||||
|
client_id: clientId,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
scope: 'user',
|
||||||
|
state: state
|
||||||
|
};
|
||||||
|
|
||||||
|
$window.location.href = authorizeUrl + '?' +
|
||||||
|
UrlUtil.serializeParameters(openIdParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks our OpenID endpoint to convert an authorization token to
|
||||||
|
* an access token.
|
||||||
|
*/
|
||||||
|
token: function (params) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var authorizationCode = params.code;
|
||||||
|
|
||||||
|
var tokenParams = {
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
code: authorizationCode
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = tokenUrl + '?' +
|
||||||
|
UrlUtil.serializeParameters(tokenParams);
|
||||||
|
|
||||||
|
$http({method: 'POST', url: url})
|
||||||
|
.then(function (response) {
|
||||||
|
$log.debug('Token creation succeeded.');
|
||||||
|
// Extract the data
|
||||||
|
var data = response.data;
|
||||||
|
|
||||||
|
// Derive an issue date, from the Date header if
|
||||||
|
// possible.
|
||||||
|
var dateHeader = response.headers('Date');
|
||||||
|
if (!dateHeader) {
|
||||||
|
data.issue_date = Math.floor(Date.now() / 1000);
|
||||||
|
} else {
|
||||||
|
data.issue_date = Math.floor(
|
||||||
|
new Date(dateHeader) / 1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deferred.resolve(data);
|
||||||
|
},
|
||||||
|
function (response) {
|
||||||
|
$log.debug('Token creation failed.');
|
||||||
|
|
||||||
|
// Construct a conformant error response.
|
||||||
|
var error = response.data;
|
||||||
|
if (!error.hasOwnProperty('error')) {
|
||||||
|
error = {
|
||||||
|
error: response.status,
|
||||||
|
error_description: response.data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
deferred.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
170
src/app/auth/service/session.js
Normal file
170
src/app/auth/service/session.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session management service - keeps track of our current session state, mostly
|
||||||
|
* by verifying the token state returned from the OpenID service.
|
||||||
|
*/
|
||||||
|
angular.module('sb.auth').factory('Session',
|
||||||
|
function (SessionState, AccessToken, $rootScope, $log, $q, User) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current session state.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
var sessionState = SessionState.PENDING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the session.
|
||||||
|
*/
|
||||||
|
function initializeSession() {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
if (!AccessToken.getAccessToken() || AccessToken.isExpired()) {
|
||||||
|
$log.debug('No token found');
|
||||||
|
updateSessionState(SessionState.LOGGED_OUT);
|
||||||
|
deferred.resolve();
|
||||||
|
} else {
|
||||||
|
// Validate the token currently in the cache.
|
||||||
|
validateToken()
|
||||||
|
.then(function () {
|
||||||
|
$log.debug('Token validated');
|
||||||
|
updateSessionState(SessionState.LOGGED_IN);
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
}, function () {
|
||||||
|
$log.debug('Token not validated');
|
||||||
|
AccessToken.clear();
|
||||||
|
updateSessionState(SessionState.LOGGED_OUT);
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the token.
|
||||||
|
*/
|
||||||
|
function validateToken() {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
var id = AccessToken.getIdToken();
|
||||||
|
|
||||||
|
User.read({id: id},
|
||||||
|
function (user) {
|
||||||
|
deferred.resolve(user);
|
||||||
|
}, function (error) {
|
||||||
|
deferred.reject(error);
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles state updates and broadcasts.
|
||||||
|
*/
|
||||||
|
function updateSessionState(newState) {
|
||||||
|
if (newState !== sessionState) {
|
||||||
|
sessionState = newState;
|
||||||
|
$rootScope.$broadcast(sessionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the session (Clear the token).
|
||||||
|
*/
|
||||||
|
function destroySession() {
|
||||||
|
AccessToken.clear();
|
||||||
|
updateSessionState(SessionState.LOGGED_OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and test our current session token.
|
||||||
|
*/
|
||||||
|
initializeSession();
|
||||||
|
|
||||||
|
// If we ever encounter a 401 error, make sure the session is destroyed.
|
||||||
|
$rootScope.$on('http_401', function () {
|
||||||
|
destroySession();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose the methods for this service.
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* The current session state.
|
||||||
|
*/
|
||||||
|
getSessionState: function () {
|
||||||
|
return sessionState;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the current session state, as a promise.
|
||||||
|
*/
|
||||||
|
resolveSessionState: function () {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
if (sessionState !== SessionState.PENDING) {
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
} else {
|
||||||
|
var unwatch = $rootScope.$watch(function () {
|
||||||
|
return sessionState;
|
||||||
|
}, function () {
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
unwatch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we logged in?
|
||||||
|
*/
|
||||||
|
isLoggedIn: function () {
|
||||||
|
return sessionState === SessionState.LOGGED_IN;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the session.
|
||||||
|
*/
|
||||||
|
destroySession: function () {
|
||||||
|
destroySession();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the session with a new (or null) token.
|
||||||
|
*/
|
||||||
|
updateSession: function (token) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
if (!token) {
|
||||||
|
destroySession();
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
} else {
|
||||||
|
AccessToken.setToken(token);
|
||||||
|
initializeSession().then(
|
||||||
|
function () {
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
deferred.resolve(sessionState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
@ -18,7 +18,5 @@
|
|||||||
* The Storyboard Services module contains all of the necessary API resources
|
* The Storyboard Services module contains all of the necessary API resources
|
||||||
* used by the storyboard client. Its resources are available via injection to
|
* used by the storyboard client. Its resources are available via injection to
|
||||||
* any module that declares it as a dependency.
|
* any module that declares it as a dependency.
|
||||||
*
|
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
*/
|
||||||
angular.module('sb.services', ['ngResource', 'ngCookies']);
|
angular.module('sb.services', ['ngResource']);
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This collection of utility methods allow us to pre-resolve AuthProvider
|
|
||||||
* resources before a UI route switch is completed.
|
|
||||||
*
|
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
|
||||||
angular.module('sb.services').constant('AuthProviderResolver', {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves all available authorization providers.
|
|
||||||
*/
|
|
||||||
resolveAuthProviders: function ($q, AuthProvider, $log) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
$log.debug('Resolving AuthProviders');
|
|
||||||
|
|
||||||
var deferred = $q.defer();
|
|
||||||
|
|
||||||
AuthProvider.query(
|
|
||||||
function (result) {
|
|
||||||
deferred.resolve(result);
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
$log.warn('Route resolution rejected for AuthProviders');
|
|
||||||
deferred.reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves an AuthProvider based on the unique ID passed via the
|
|
||||||
* stateParams.
|
|
||||||
*/
|
|
||||||
resolveAuthProvider: function (stateParamName) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return function ($q, AuthProvider, $stateParams, $log) {
|
|
||||||
|
|
||||||
var deferred = $q.defer();
|
|
||||||
|
|
||||||
if (!$stateParams.hasOwnProperty(stateParamName)) {
|
|
||||||
$log.warn('State did not contain property of name ' +
|
|
||||||
stateParamName);
|
|
||||||
|
|
||||||
deferred.reject({
|
|
||||||
'error': true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var id = $stateParams[stateParamName];
|
|
||||||
|
|
||||||
$log.debug('Resolving AuthProvider: ' + id);
|
|
||||||
|
|
||||||
AuthProvider.get({'id': id},
|
|
||||||
function (result) {
|
|
||||||
deferred.resolve(result);
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
$log.warn('Route resolution rejected for ' +
|
|
||||||
'AuthProvider ' + id);
|
|
||||||
deferred.reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -18,10 +18,35 @@
|
|||||||
* Controller for our application header.
|
* Controller for our application header.
|
||||||
*/
|
*/
|
||||||
angular.module('storyboard').controller('HeaderController',
|
angular.module('storyboard').controller('HeaderController',
|
||||||
function ($scope, $modal, NewStoryService) {
|
function ($scope, NewStoryService, Session, SessionState, CurrentUser) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and maintain the current user.
|
||||||
|
*/
|
||||||
|
$scope.currentUser = CurrentUser.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new story.
|
||||||
|
*/
|
||||||
$scope.newStory = function () {
|
$scope.newStory = function () {
|
||||||
NewStoryService.showNewStoryModal();
|
NewStoryService.showNewStoryModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View handle to show the current logged in state.
|
||||||
|
*/
|
||||||
|
$scope.isLoggedIn =
|
||||||
|
(Session.getSessionState() === SessionState.LOGGED_IN);
|
||||||
|
|
||||||
|
// Watch for changes to the session state.
|
||||||
|
$scope.$watch(
|
||||||
|
function () {
|
||||||
|
return Session.getSessionState();
|
||||||
|
},
|
||||||
|
function (sessionState) {
|
||||||
|
$scope.isLoggedIn = sessionState === SessionState.LOGGED_IN;
|
||||||
|
$scope.currentUser = CurrentUser.get();
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -48,13 +48,13 @@ angular.module('storyboard',
|
|||||||
$httpProvider.defaults.headers.common['X-Client'] = 'Storyboard';
|
$httpProvider.defaults.headers.common['X-Client'] = 'Storyboard';
|
||||||
|
|
||||||
})
|
})
|
||||||
.run(function ($log, $rootScope, $location) {
|
.run(function ($log, $rootScope, $state) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Listen to changes on the root scope. If it's an error in the state
|
// Listen to changes on the root scope. If it's an error in the state
|
||||||
// changes (i.e. a 404) take the user back to the index.
|
// changes (i.e. a 404) take the user back to the index.
|
||||||
$rootScope.$on('$stateChangeError',
|
$rootScope.$on('$stateChangeError',
|
||||||
function () {
|
function () {
|
||||||
$location.path('/');
|
$state.go('index');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
~
|
~
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License"); you may
|
~ 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
|
~ not use this file except in compliance with the License. You may obtain
|
||||||
@ -17,10 +17,8 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<h1>Login with {{authProvider.title}}</h1>
|
<p class="text-center">
|
||||||
<p class="lead">
|
<i class="fa fa-spinner fa-lg fa-spin"></i>
|
||||||
This feature requires the existence of a functioning API
|
|
||||||
Authentication layer, and is therefore disabled.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
42
src/app/templates/auth/error.html
Normal file
42
src/app/templates/auth/error.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h1>Oh no!</h1>
|
||||||
|
|
||||||
|
<p class="lead">We encountered an unexpected error while trying to
|
||||||
|
log you in. The error message below should be helpful,
|
||||||
|
though if it's not you can contact our engineers in
|
||||||
|
#storyboard on
|
||||||
|
<a href="http://freenode.net/" target="_blank">
|
||||||
|
Freenode
|
||||||
|
</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<dl class="dl-horizontal text-danger">
|
||||||
|
<dt>Error Code:</dt>
|
||||||
|
<dd>{{error}}</dd>
|
||||||
|
<dt>Error Description:</dt>
|
||||||
|
<dd>{{errorDescription}}</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<!-- TODO(krotscheck): If a user reaches this point, they should
|
||||||
|
be easily able to submit a bug report to storyboard -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,33 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
~
|
|
||||||
~ 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<h1>How would you like to log in?</h1>
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-8 col-xs-12">
|
|
||||||
<a ng-repeat="provider in authProviders"
|
|
||||||
ng-class="[provider.type]"
|
|
||||||
class="auth-provider btn btn-info btn-lg btn-block"
|
|
||||||
href="#!/auth/provider/{{provider.id}}">
|
|
||||||
<i class="fa fa-caret-right"></i> {{provider.title}}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -46,8 +46,8 @@
|
|||||||
<li class="visible-xs">
|
<li class="visible-xs">
|
||||||
<p class="navbar-text" ng-show="isLoggedIn">
|
<p class="navbar-text" ng-show="isLoggedIn">
|
||||||
<i class="fa fa-user"></i>
|
<i class="fa fa-user"></i>
|
||||||
{{currentUser.firstName}}
|
{{currentUser.first_name}}
|
||||||
{{currentUser.lastName}}
|
{{currentUser.last_name}}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@ -67,10 +67,10 @@
|
|||||||
</li>
|
</li>
|
||||||
<!-- Login/Logout button, XS only. -->
|
<!-- Login/Logout button, XS only. -->
|
||||||
<li class="visible-xs">
|
<li class="visible-xs">
|
||||||
<a href="#!/auth/login" ng-hide="isLoggedIn">
|
<a href="#!/auth/authorize" ng-hide="isLoggedIn">
|
||||||
Log in
|
Log in
|
||||||
</a>
|
</a>
|
||||||
<a href="#!/auth/logout" ng-show="isLoggedIn">
|
<a href="#!/auth/deauthorize" ng-show="isLoggedIn">
|
||||||
Log out
|
Log out
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -90,19 +90,19 @@
|
|||||||
<li ng-show="isLoggedIn">
|
<li ng-show="isLoggedIn">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
<i class="fa fa-user"></i>
|
<i class="fa fa-user"></i>
|
||||||
{{currentUser.firstName}}
|
{{currentUser.first_name}}
|
||||||
{{currentUser.lastName}}
|
{{currentUser.last_name}}
|
||||||
<i class="fa fa-caret-down"></i>
|
<i class="fa fa-caret-down"></i>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a href="#!/auth/logout">Logout</a>
|
<a href="#!/auth/deauthorize">Logout</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<!-- Login, non-XS only. -->
|
<!-- Login, non-XS only. -->
|
||||||
<li ng-hide="isLoggedIn">
|
<li ng-hide="isLoggedIn">
|
||||||
<a href="#!/auth">Log in</a>
|
<a href="#!/auth/authorize">Log in</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
65
src/app/util/helpers/string_util.js
Normal file
65
src/app/util/helpers/string_util.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Mirantis 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A collection of string utilities.
|
||||||
|
*
|
||||||
|
* @author Nikita Konovalov
|
||||||
|
*/
|
||||||
|
|
||||||
|
angular.module('sb.util').factory('StringUtil',
|
||||||
|
function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Helper to generate a random alphanumeric string for the state
|
||||||
|
* parameter.
|
||||||
|
*
|
||||||
|
* @param length The length of the string to generate.
|
||||||
|
* @returns {string} A random alphanumeric string.
|
||||||
|
*/
|
||||||
|
randomAlphaNumeric: function (length) {
|
||||||
|
var possible =
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
||||||
|
'abcdefghijklmnopqrstuvwxyz' +
|
||||||
|
'0123456789';
|
||||||
|
|
||||||
|
return this.random(length, possible);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to generate a random string of specified length, using a
|
||||||
|
* provided list of characters.
|
||||||
|
*
|
||||||
|
* @param length The length of the string to generate.
|
||||||
|
* @param characters The list of valid characters.
|
||||||
|
* @returns {string} A random string composed of provided
|
||||||
|
* characters.
|
||||||
|
*/
|
||||||
|
random: function (length, characters) {
|
||||||
|
var text = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
text += characters.charAt(Math.floor(
|
||||||
|
Math.random() * characters.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
87
src/app/util/helpers/url_util.js
Normal file
87
src/app/util/helpers/url_util.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL and location manipulation utilities.
|
||||||
|
*
|
||||||
|
* @author Nikita Konovalov
|
||||||
|
*/
|
||||||
|
angular.module('sb.util').factory('UrlUtil',
|
||||||
|
function ($location) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Return the full URL prefix of the application, without the #!
|
||||||
|
* component.
|
||||||
|
*/
|
||||||
|
getFullUrlPrefix: function () {
|
||||||
|
var protocol = $location.protocol();
|
||||||
|
var host = $location.host();
|
||||||
|
var port = $location.port();
|
||||||
|
|
||||||
|
return protocol + '://' + host + ':' + port;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a HashBang url for this application given the provided
|
||||||
|
* fragment.
|
||||||
|
*/
|
||||||
|
buildApplicationUrl: function (fragment) {
|
||||||
|
return this.getFullUrlPrefix() + '/#!' + fragment;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an object into HTTP parameters.
|
||||||
|
*/
|
||||||
|
serializeParameters: function (params) {
|
||||||
|
var pairs = [];
|
||||||
|
for (var prop in params) {
|
||||||
|
// Filter out system params.
|
||||||
|
if (!params.hasOwnProperty(prop)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pairs.push(
|
||||||
|
encodeURIComponent(prop) +
|
||||||
|
'=' +
|
||||||
|
encodeURIComponent(params[prop])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return pairs.join('&');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize URI query parameters into an object.
|
||||||
|
*/
|
||||||
|
deserializeParameters: function (queryString) {
|
||||||
|
|
||||||
|
var params = {};
|
||||||
|
var queryComponents = queryString.split('&');
|
||||||
|
for (var i = 0; i < queryComponents.length; i++) {
|
||||||
|
var parts = queryComponents[i].split('=');
|
||||||
|
var key = decodeURIComponent(parts[0]) || null;
|
||||||
|
var value = decodeURIComponent(parts[1]) || null;
|
||||||
|
|
||||||
|
if (!!key && !!value) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
@ -15,19 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This controller handles the logic for the authorization provider list page.
|
* Utility injector, injects the query parameters from the NON-hashbang URL as
|
||||||
*
|
* $searchParams.
|
||||||
* @author Michael Krotscheck
|
|
||||||
*/
|
*/
|
||||||
angular.module('sb.auth').controller('AuthListController',
|
angular.module('sb.util').factory('$searchParams',
|
||||||
function ($scope, authProviders, $state) {
|
function ($window, UrlUtil) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// If there's only one auth provider, just use that.
|
var params = {};
|
||||||
if (!!authProviders && authProviders.length === 1) {
|
var search = $window.location.search;
|
||||||
$state.go('auth.provider.id', {id: authProviders[0].id});
|
if (!!search) {
|
||||||
|
if (search.charAt(0) === '?') {
|
||||||
|
search = search.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.authProviders = authProviders;
|
return UrlUtil.deserializeParameters(search);
|
||||||
|
}
|
||||||
|
return params;
|
||||||
});
|
});
|
@ -34,8 +34,7 @@
|
|||||||
<script src="angular-bootstrap/ui-bootstrap-tpls.js"></script>
|
<script src="angular-bootstrap/ui-bootstrap-tpls.js"></script>
|
||||||
<script src="angular-ui-router/release/angular-ui-router.js"></script>
|
<script src="angular-ui-router/release/angular-ui-router.js"></script>
|
||||||
<script src="angular-resource/angular-resource.js"></script>
|
<script src="angular-resource/angular-resource.js"></script>
|
||||||
<script src="angular-mocks/angular-mocks.js"></script>
|
<script src="angular-local-storage/angular-local-storage.js"></script>
|
||||||
<script src="angular-cookies/angular-cookies.js"></script>
|
|
||||||
<script src="angular-sanitize/angular-sanitize.js"></script>
|
<script src="angular-sanitize/angular-sanitize.js"></script>
|
||||||
|
|
||||||
<script src="bootstrap/dist/js/bootstrap.js"></script>
|
<script src="bootstrap/dist/js/bootstrap.js"></script>
|
||||||
|
@ -34,7 +34,7 @@ describe('sb.services', function () {
|
|||||||
expect(module).toBeTruthy();
|
expect(module).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load cookies module', function () {
|
it('should load resource module', function () {
|
||||||
expect(hasModule('ngCookies')).toBeTruthy();
|
expect(hasModule('ngResource')).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user