ported CSRF fix to 1.0 using sessions
parent
2928240587
commit
0d530a67d4
|
@ -21,6 +21,9 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import net.minidev.json.JSONObject;
|
import net.minidev.json.JSONObject;
|
||||||
|
|
||||||
|
@ -45,6 +48,8 @@ import org.springframework.security.oauth2.provider.AuthorizationRequestManager;
|
||||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||||
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
|
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.nimbusds.jose.util.JSONObjectUtils;
|
import com.nimbusds.jose.util.JSONObjectUtils;
|
||||||
|
@ -123,6 +128,15 @@ public class ConnectAuthorizationRequestManager implements AuthorizationRequestM
|
||||||
// note that we have to inject the processed parameters in at this point so that SECOAUTH can find them later (and this object will get copy-constructored away anyway)
|
// note that we have to inject the processed parameters in at this point so that SECOAUTH can find them later (and this object will get copy-constructored away anyway)
|
||||||
DefaultAuthorizationRequest request = new DefaultAuthorizationRequest(parameters, Collections.<String, String> emptyMap(), clientId, scopes);
|
DefaultAuthorizationRequest request = new DefaultAuthorizationRequest(parameters, Collections.<String, String> emptyMap(), clientId, scopes);
|
||||||
request.addClientDetails(client);
|
request.addClientDetails(client);
|
||||||
|
|
||||||
|
// get the session so we can store a CSRF protection value in it
|
||||||
|
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpSession session = attr.getRequest().getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
String csrf = UUID.randomUUID().toString();
|
||||||
|
session.setAttribute("csrf", csrf);
|
||||||
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.mitre.openid.connect.model.ApprovedSite;
|
import org.mitre.openid.connect.model.ApprovedSite;
|
||||||
import org.mitre.openid.connect.model.WhitelistedSite;
|
import org.mitre.openid.connect.model.WhitelistedSite;
|
||||||
import org.mitre.openid.connect.service.ApprovedSiteService;
|
import org.mitre.openid.connect.service.ApprovedSiteService;
|
||||||
|
@ -34,6 +36,8 @@ import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||||
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
|
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
|
||||||
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
|
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
@ -90,7 +94,17 @@ public class TofuUserApprovalHandler implements UserApprovalHandler {
|
||||||
// TODO: make parameter name configurable?
|
// TODO: make parameter name configurable?
|
||||||
boolean approved = Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval"));
|
boolean approved = Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval"));
|
||||||
|
|
||||||
return userAuthentication.isAuthenticated() && approved;
|
boolean csrfApproved = false;
|
||||||
|
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpSession session = attr.getRequest().getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
String csrf = (String) session.getAttribute("csrf");
|
||||||
|
if (csrf != null && csrf.equals(authorizationRequest.getApprovalParameters().get("csrf"))) {
|
||||||
|
csrfApproved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userAuthentication.isAuthenticated() && approved && csrfApproved;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -173,49 +187,66 @@ public class TofuUserApprovalHandler implements UserApprovalHandler {
|
||||||
|
|
||||||
if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) {
|
if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) {
|
||||||
|
|
||||||
// TODO: Get SECOAUTH to stop breaking polymorphism and start using real objects, SRSLY
|
// get the session so we can store a CSRF protection value in it
|
||||||
DefaultAuthorizationRequest ar = new DefaultAuthorizationRequest(authorizationRequest);
|
boolean csrfApproved = false;
|
||||||
|
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpSession session = attr.getRequest().getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
String csrf = (String) session.getAttribute("csrf");
|
||||||
|
if (csrf != null && csrf.equals(authorizationRequest.getApprovalParameters().get("csrf"))) {
|
||||||
|
csrfApproved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// process scopes from user input
|
if (csrfApproved) {
|
||||||
Set<String> allowedScopes = Sets.newHashSet();
|
|
||||||
Map<String,String> approvalParams = ar.getApprovalParameters();
|
|
||||||
|
|
||||||
Set<String> keys = approvalParams.keySet();
|
// TODO: Get SECOAUTH to stop breaking polymorphism and start using real objects, SRSLY
|
||||||
|
DefaultAuthorizationRequest ar = new DefaultAuthorizationRequest(authorizationRequest);
|
||||||
|
|
||||||
for (String key : keys) {
|
// process scopes from user input
|
||||||
if (key.startsWith("scope_")) {
|
Set<String> allowedScopes = Sets.newHashSet();
|
||||||
//This is a scope parameter from the approval page. The value sent back should
|
Map<String,String> approvalParams = ar.getApprovalParameters();
|
||||||
//be the scope string. Check to make sure it is contained in the client's
|
|
||||||
//registered allowed scopes.
|
|
||||||
|
|
||||||
String scope = approvalParams.get(key);
|
Set<String> keys = approvalParams.keySet();
|
||||||
|
|
||||||
//Make sure this scope is allowed for the given client
|
for (String key : keys) {
|
||||||
if (client.getScope().contains(scope)) {
|
if (key.startsWith("scope_")) {
|
||||||
allowedScopes.add(scope);
|
//This is a scope parameter from the approval page. The value sent back should
|
||||||
|
//be the scope string. Check to make sure it is contained in the client's
|
||||||
|
//registered allowed scopes.
|
||||||
|
|
||||||
|
String scope = approvalParams.get(key);
|
||||||
|
|
||||||
|
//Make sure this scope is allowed for the given client
|
||||||
|
if (client.getScope().contains(scope)) {
|
||||||
|
allowedScopes.add(scope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// inject the user-allowed scopes into the auth request
|
// inject the user-allowed scopes into the auth request
|
||||||
ar.setScope(allowedScopes);
|
ar.setScope(allowedScopes);
|
||||||
|
|
||||||
//Only store an ApprovedSite if the user has checked "remember this decision":
|
//Only store an ApprovedSite if the user has checked "remember this decision":
|
||||||
String remember = ar.getApprovalParameters().get("remember");
|
String remember = ar.getApprovalParameters().get("remember");
|
||||||
if (!Strings.isNullOrEmpty(remember) && !remember.equals("none")) {
|
if (!Strings.isNullOrEmpty(remember) && !remember.equals("none")) {
|
||||||
|
|
||||||
Date timeout = null;
|
Date timeout = null;
|
||||||
if (remember.equals("one-hour")) {
|
if (remember.equals("one-hour")) {
|
||||||
// set the timeout to one hour from now
|
// set the timeout to one hour from now
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
cal.add(Calendar.HOUR, 1);
|
cal.add(Calendar.HOUR, 1);
|
||||||
timeout = cal.getTime();
|
timeout = cal.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
approvedSiteService.createApprovedSite(clientId, userId, timeout, allowedScopes, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
approvedSiteService.createApprovedSite(clientId, userId, timeout, allowedScopes, null);
|
return ar;
|
||||||
}
|
} else {
|
||||||
|
// csrf didn't match, it's not approved, pass through
|
||||||
return ar;
|
return authorizationRequest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authorizationRequest;
|
return authorizationRequest;
|
||||||
|
|
|
@ -115,6 +115,7 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input id="user_oauth_approval" name="user_oauth_approval" value="true" type="hidden"/>
|
<input id="user_oauth_approval" name="user_oauth_approval" value="true" type="hidden"/>
|
||||||
|
<input name="csrf" value="${ sessionScope.csrf }" type="hidden" />
|
||||||
<input name="authorize" value="Authorize" type="submit"
|
<input name="authorize" value="Authorize" type="submit"
|
||||||
onclick="$('#user_oauth_approval').attr('value',true)" class="btn btn-success btn-large"/>
|
onclick="$('#user_oauth_approval').attr('value',true)" class="btn btn-success btn-large"/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue