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",
|
||||
"angular": "1.2.13",
|
||||
"angular-resource": "1.2.13",
|
||||
"angular-cookies": "1.2.13",
|
||||
"angular-sanitize": "1.2.13",
|
||||
"bootstrap": "3.1.0",
|
||||
"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": {
|
||||
"angular-mocks": "1.2.13",
|
||||
|
@ -35,7 +35,10 @@ module.exports = function (config) {
|
||||
],
|
||||
|
||||
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'
|
||||
],
|
||||
|
||||
|
@ -35,7 +35,10 @@ module.exports = function (config) {
|
||||
],
|
||||
|
||||
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'
|
||||
],
|
||||
|
||||
|
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();
|
||||
});
|
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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
|
||||
@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This controller handles the logic for the authorization provider list page.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
* This controller deauthorizes the session and destroys all tokens.
|
||||
*/
|
||||
angular.module('sb.auth').controller('AuthLoginController',
|
||||
function ($scope, authProvider) {
|
||||
|
||||
angular.module('sb.auth').controller('AuthDeauthorizeController',
|
||||
function (Session, $state, $log) {
|
||||
'use strict';
|
||||
|
||||
$scope.authProvider = authProvider;
|
||||
|
||||
});
|
||||
$log.debug('Logging out');
|
||||
Session.destroySession();
|
||||
$state.go('index');
|
||||
});
|
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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
|
||||
@ -15,19 +15,16 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This resource exposes authorization providers to our angularjs environment,
|
||||
* 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
|
||||
* View controller for authorization error conditions.
|
||||
*/
|
||||
|
||||
angular.module('sb.services').factory('AuthProvider',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
angular.module('sb.auth').controller('AuthErrorController',
|
||||
function ($scope, $stateParams) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/auth/provider/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
console.warn('AuthErrorController');
|
||||
|
||||
|
||||
$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
|
||||
* logic.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
* This Storyboard module contains our authentication and authorization logic.
|
||||
*/
|
||||
angular.module('sb.auth',
|
||||
[ 'sb.services', 'sb.templates', 'ui.router']
|
||||
angular.module('sb.auth', [ 'sb.services', 'sb.templates', 'ui.router',
|
||||
'sb.util', 'LocalStorageModule']
|
||||
)
|
||||
.config(function ($stateProvider, $urlRouterProvider,
|
||||
AuthProviderResolver) {
|
||||
.config(function ($stateProvider, SessionResolver) {
|
||||
'use strict';
|
||||
|
||||
// Default rerouting.
|
||||
$urlRouterProvider.when('/auth', '/auth/provider/list');
|
||||
$urlRouterProvider.when('/auth/provider', '/auth/provider/list');
|
||||
|
||||
// Declare the states for this module.
|
||||
$stateProvider
|
||||
.state('auth', {
|
||||
abstract: true,
|
||||
url: '/auth',
|
||||
template: '<div ui-view></div>'
|
||||
template: '<div ui-view></div>',
|
||||
url: '/auth'
|
||||
})
|
||||
.state('auth.provider', {
|
||||
abstract: true,
|
||||
url: '/provider',
|
||||
template: '<div ui-view></div>'
|
||||
})
|
||||
.state('auth.provider.list', {
|
||||
url: '/list',
|
||||
templateUrl: 'app/templates/auth/provider/list.html',
|
||||
controller: 'AuthListController',
|
||||
.state('auth.authorize', {
|
||||
url: '/authorize?error&error_description',
|
||||
templateUrl: 'app/templates/auth/busy.html',
|
||||
controller: 'AuthAuthorizeController',
|
||||
resolve: {
|
||||
authProviders: AuthProviderResolver.resolveAuthProviders
|
||||
isLoggedOut: SessionResolver.requireLoggedOut
|
||||
}
|
||||
})
|
||||
.state('auth.provider.id', {
|
||||
url: '/:id',
|
||||
templateUrl: 'app/templates/auth/provider/login.html',
|
||||
controller: 'AuthLoginController',
|
||||
.state('auth.deauthorize', {
|
||||
url: '/deauthorize',
|
||||
templateUrl: 'app/templates/auth/busy.html',
|
||||
controller: 'AuthDeauthorizeController',
|
||||
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
|
||||
* used by the storyboard client. Its resources are available via injection to
|
||||
* 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.
|
||||
*/
|
||||
angular.module('storyboard').controller('HeaderController',
|
||||
function ($scope, $modal, NewStoryService) {
|
||||
function ($scope, NewStoryService, Session, SessionState, CurrentUser) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Load and maintain the current user.
|
||||
*/
|
||||
$scope.currentUser = CurrentUser.get();
|
||||
|
||||
/**
|
||||
* Create a new story.
|
||||
*/
|
||||
$scope.newStory = function () {
|
||||
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';
|
||||
|
||||
})
|
||||
.run(function ($log, $rootScope, $location) {
|
||||
.run(function ($log, $rootScope, $state) {
|
||||
'use strict';
|
||||
|
||||
// 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.
|
||||
$rootScope.$on('$stateChangeError',
|
||||
function () {
|
||||
$location.path('/');
|
||||
$state.go('index');
|
||||
});
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
<!--
|
||||
~ 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
|
||||
~ 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
|
||||
~ 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
|
||||
@ -17,10 +17,8 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Login with {{authProvider.title}}</h1>
|
||||
<p class="lead">
|
||||
This feature requires the existence of a functioning API
|
||||
Authentication layer, and is therefore disabled.
|
||||
<p class="text-center">
|
||||
<i class="fa fa-spinner fa-lg fa-spin"></i>
|
||||
</p>
|
||||
</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">
|
||||
<p class="navbar-text" ng-show="isLoggedIn">
|
||||
<i class="fa fa-user"></i>
|
||||
{{currentUser.firstName}}
|
||||
{{currentUser.lastName}}
|
||||
{{currentUser.first_name}}
|
||||
{{currentUser.last_name}}
|
||||
</p>
|
||||
</li>
|
||||
|
||||
@ -67,10 +67,10 @@
|
||||
</li>
|
||||
<!-- Login/Logout button, XS only. -->
|
||||
<li class="visible-xs">
|
||||
<a href="#!/auth/login" ng-hide="isLoggedIn">
|
||||
<a href="#!/auth/authorize" ng-hide="isLoggedIn">
|
||||
Log in
|
||||
</a>
|
||||
<a href="#!/auth/logout" ng-show="isLoggedIn">
|
||||
<a href="#!/auth/deauthorize" ng-show="isLoggedIn">
|
||||
Log out
|
||||
</a>
|
||||
</li>
|
||||
@ -90,19 +90,19 @@
|
||||
<li ng-show="isLoggedIn">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-user"></i>
|
||||
{{currentUser.firstName}}
|
||||
{{currentUser.lastName}}
|
||||
{{currentUser.first_name}}
|
||||
{{currentUser.last_name}}
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="#!/auth/logout">Logout</a>
|
||||
<a href="#!/auth/deauthorize">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- Login, non-XS only. -->
|
||||
<li ng-hide="isLoggedIn">
|
||||
<a href="#!/auth">Log in</a>
|
||||
<a href="#!/auth/authorize">Log in</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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
|
||||
@ -15,19 +15,21 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This controller handles the logic for the authorization provider list page.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
* Utility injector, injects the query parameters from the NON-hashbang URL as
|
||||
* $searchParams.
|
||||
*/
|
||||
angular.module('sb.auth').controller('AuthListController',
|
||||
function ($scope, authProviders, $state) {
|
||||
angular.module('sb.util').factory('$searchParams',
|
||||
function ($window, UrlUtil) {
|
||||
'use strict';
|
||||
|
||||
// If there's only one auth provider, just use that.
|
||||
if (!!authProviders && authProviders.length === 1) {
|
||||
$state.go('auth.provider.id', {id: authProviders[0].id});
|
||||
var params = {};
|
||||
var search = $window.location.search;
|
||||
if (!!search) {
|
||||
if (search.charAt(0) === '?') {
|
||||
search = search.substr(1);
|
||||
}
|
||||
|
||||
return UrlUtil.deserializeParameters(search);
|
||||
}
|
||||
|
||||
$scope.authProviders = authProviders;
|
||||
|
||||
});
|
||||
return params;
|
||||
});
|
@ -34,8 +34,7 @@
|
||||
<script src="angular-bootstrap/ui-bootstrap-tpls.js"></script>
|
||||
<script src="angular-ui-router/release/angular-ui-router.js"></script>
|
||||
<script src="angular-resource/angular-resource.js"></script>
|
||||
<script src="angular-mocks/angular-mocks.js"></script>
|
||||
<script src="angular-cookies/angular-cookies.js"></script>
|
||||
<script src="angular-local-storage/angular-local-storage.js"></script>
|
||||
<script src="angular-sanitize/angular-sanitize.js"></script>
|
||||
|
||||
<script src="bootstrap/dist/js/bootstrap.js"></script>
|
||||
|
@ -34,7 +34,7 @@ describe('sb.services', function () {
|
||||
expect(module).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should load cookies module', function () {
|
||||
expect(hasModule('ngCookies')).toBeTruthy();
|
||||
it('should load resource module', function () {
|
||||
expect(hasModule('ngResource')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user