Skip to content

Commit

Permalink
Merge pull request #8958 from liggitt/session-storage-1.2
Browse files Browse the repository at this point in the history
CSRF updates - 1.2.0
  • Loading branch information
smarterclayton committed May 25, 2016
2 parents e0b4ef6 + 56ebf57 commit 2e62fab
Show file tree
Hide file tree
Showing 10 changed files with 476 additions and 51 deletions.
48 changes: 39 additions & 9 deletions assets/app/scripts/controllers/util/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@
* Controller of the openshiftConsole
*/
angular.module('openshiftConsole')
.controller('OAuthController', function ($location, $q, RedirectLoginService, DataService, AuthService, Logger) {
.controller('OAuthController', function ($scope, $location, $q, RedirectLoginService, DataService, AuthService, Logger) {
var authLogger = Logger.get("auth");

// Initialize to a no-op function.
// Needed to let the view confirm a login when the state is unverified.
$scope.completeLogin = function(){};
$scope.cancelLogin = function() {
$location.replace();
$location.url("./");
};

RedirectLoginService.finish()
.then(function(data) {
var token = data.token;
var then = data.then;
var verified = data.verified;
var ttl = data.ttl;

// Try to fetch the user
Expand All @@ -25,21 +34,41 @@ angular.module('openshiftConsole')
.then(function(user) {
// Set the new user and token in the auth service
authLogger.log("OAuthController, got user", user);
AuthService.setUser(user, token, ttl);

// Redirect to original destination (or default to '/')
var destination = then || './';
if (URI(destination).is('absolute')) {
authLogger.log("OAuthController, invalid absolute redirect", destination);
destination = './';
$scope.completeLogin = function() {
// Persist the user
AuthService.setUser(user, token, ttl);

// Redirect to original destination (or default to './')
var destination = then || './';
if (URI(destination).is('absolute')) {
authLogger.log("OAuthController, invalid absolute redirect", destination);
destination = './';
}
authLogger.log("OAuthController, redirecting", destination);
$location.replace();
$location.url(destination);
};

if (verified) {
// Automatically complete
$scope.completeLogin();
} else {
// Require the UI to prompt
$scope.confirmUser = user;

// Additionally, give the UI info about the user being overridden
var currentUser = AuthService.UserStore().getUser();
if (currentUser && currentUser.metadata.name !== user.metadata.name) {
$scope.overriddenUser = currentUser;
}
}
authLogger.log("OAuthController, redirecting", destination);
$location.url(destination);
})
.catch(function(rejection) {
// Handle an API error response fetching the user
var redirect = URI('error').query({error: 'user_fetch_failed'}).toString();
authLogger.error("OAuthController, error fetching user", rejection, "redirecting", redirect);
$location.replace();
$location.url(redirect);
});

Expand All @@ -51,6 +80,7 @@ angular.module('openshiftConsole')
error_uri: rejection.error_uri || ""
}).toString();
authLogger.error("OAuthController, error", rejection, "redirecting", redirect);
$location.replace();
$location.url(redirect);
});

Expand Down
83 changes: 75 additions & 8 deletions assets/app/scripts/services/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,70 @@ angular.module('openshiftConsole')
this.$get = function($location, $q, Logger) {
var authLogger = Logger.get("auth");

var getRandomInts = function(length) {
var randomValues;

if (window.crypto && window.Uint32Array) {
try {
var r = new Uint32Array(length);
window.crypto.getRandomValues(r);
randomValues = [];
for (var j=0; j < length; j++) {
randomValues.push(r[j]);
}
} catch(e) {
authLogger.debug("RedirectLoginService.getRandomInts: ", e);
randomValues = null;
}
}

if (!randomValues) {
randomValues = [];
for (var i=0; i < length; i++) {
randomValues.push(Math.floor(Math.random() * 4294967296));
}
}

return randomValues;
};

var nonceKey = "RedirectLoginService.nonce";
var makeState = function(then) {
var nonce = String(new Date().getTime()) + "-" + getRandomInts(8).join("");
try {
window.localStorage[nonceKey] = nonce;
} catch(e) {
authLogger.log("RedirectLoginService.makeState, localStorage error: ", e);
}
return JSON.stringify({then: then, nonce:nonce});
};
var parseState = function(state) {
var retval = {
then: null,
verified: false
};

var nonce = "";
try {
nonce = window.localStorage[nonceKey];
window.localStorage.removeItem(nonceKey);
} catch(e) {
authLogger.log("RedirectLoginService.parseState, localStorage error: ", e);
}

try {
var data = state ? JSON.parse(state) : {};
if (data && data.nonce && nonce && data.nonce === nonce) {
retval.verified = true;
retval.then = data.then;
}
} catch(e) {
authLogger.error("RedirectLoginService.parseState, state error: ", e);
}
authLogger.error("RedirectLoginService.parseState", retval);
return retval;
};

return {
// Returns a promise that resolves with {user:{...}, token:'...', ttl:X}, or rejects with {error:'...'[,error_description:'...',error_uri:'...']}
login: function() {
Expand All @@ -49,7 +113,7 @@ angular.module('openshiftConsole')
uri.query({
client_id: _oauth_client_id,
response_type: 'token',
state: returnUri.toString(),
state: makeState(returnUri.toString()),
redirect_uri: _oauth_redirect_uri
});
authLogger.log("RedirectLoginService.login(), redirecting", uri.toString());
Expand All @@ -59,7 +123,7 @@ angular.module('openshiftConsole')
},

// Parses oauth callback parameters from window.location
// Returns a promise that resolves with {token:'...',then:'...'}, or rejects with {error:'...'[,error_description:'...',error_uri:'...']}
// Returns a promise that resolves with {token:'...',then:'...',verified:true|false}, or rejects with {error:'...'[,error_description:'...',error_uri:'...']}
// If no token and no error is present, resolves with {}
// Example error codes: https://tools.ietf.org/html/rfc6749#section-5.2
finish: function() {
Expand All @@ -71,12 +135,12 @@ angular.module('openshiftConsole')
var fragmentParams = new URI("?" + u.fragment()).query(true);
authLogger.log("RedirectLoginService.finish()", queryParams, fragmentParams);

// Error codes can come in query params or fragment params
// Handle an error response from the OAuth server
// Error codes can come in query params or fragment params
// Handle an error response from the OAuth server
var error = queryParams.error || fragmentParams.error;
if (error) {
var error_description = queryParams.error_description || fragmentParams.error_description;
var error_uri = queryParams.error_uri || fragmentParams.error_uri;
if (error) {
var error_description = queryParams.error_description || fragmentParams.error_description;
var error_uri = queryParams.error_uri || fragmentParams.error_uri;
authLogger.log("RedirectLoginService.finish(), error", error, error_description, error_uri);
return $q.reject({
error: error,
Expand All @@ -85,13 +149,16 @@ angular.module('openshiftConsole')
});
}

var stateData = parseState(fragmentParams.state);

// Handle an access_token response
if (fragmentParams.access_token && (fragmentParams.token_type || "").toLowerCase() === "bearer") {
var deferred = $q.defer();
deferred.resolve({
token: fragmentParams.access_token,
ttl: fragmentParams.expires_in,
then: fragmentParams.state
then: stateData.state,
verified: stateData.verified
});
return deferred.promise;
}
Expand Down
22 changes: 18 additions & 4 deletions assets/app/views/util/oauth.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@
<div class="wrap no-sidebar">
<div class="middle surface-shaded">
<div class="container surface-shaded">
<div>
<div ng-if="!confirmUser">
<h1 style="margin-top: 10px;">Logging in&hellip;</h1>
<div>
Please wait while you are logged in...
</div>
<p>Please wait while you are logged in&hellip;</p>
</div>

<div ng-if="confirmUser && !overriddenUser">
<h1 style="margin-top: 10px;">Confirm Login</h1>
<p>You are being logged in as <code>{{confirmUser.metadata.name}}</code>.</p>
<button class="btn btn-lg btn-primary" type="button" ng-click="completeLogin();">Continue</button>
<button class="btn btn-lg btn-default" type="button" ng-click="cancelLogin();">Cancel</button>
</div>

<div ng-if="confirmUser && overriddenUser">
<h1 style="margin-top: 10px;">Confirm User Change</h1>
<p>You are about to change users from <code>{{overriddenUser.metadata.name}}</code> to <code>{{confirmUser.metadata.name}}</code>.</p>
<p>If this is unexpected, click Cancel. This could be an attempt to trick you into acting as another user.</p>
<button class="btn btn-lg btn-danger" type="button" ng-click="completeLogin();">Switch Users</button>
<button class="btn btn-lg btn-primary" type="button" ng-click="cancelLogin();">Cancel</button>
</div>

</div>
</div>
</div>
Loading

0 comments on commit 2e62fab

Please sign in to comment.