/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.oauth.internal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.restlet.ext.oauth.ResponseType;
/**
* Helper class to establish an authentication session. The session is created
* in the AuthorizationResource on initial OAuth request.
*
* At the moment it is not being cleaned up on the server side.
*
* The cookie that is set will get removed when the browser closes the window.
*
* @author Kristoffer Gronowski
* @author Shotaro Uchida <fantom@xmaker.mx>
*/
public class AuthSession {
private static final String CALLBACK = "callback";
private static final String CLIENT_ID = "client_id";
public static final int DEFAULT_TIMEOUT_SEC = 600;
private static final String FLOW = "flow";
private static final String GRANTED_SCOPE = "granted_scope";
private static final String ID = "id";
private static final String LAST_ACTIVITY = "last_activity";
private static final String OWNER = "owner";
private static final String REQ_SCOPE = "requested_scope";
private static final String STATE = "state";
private static final String TIMEOUT_SEC = "timeout_sec";
/**
* Instantiate new authorization session.
*
* @return a new authorization session.
*/
public static AuthSession newAuthSession() {
AuthSession session = new AuthSession();
// XXX: Is UUID a non-guessable value? (10.12. Cross-Site Request
// Forgery)
String sessionId = UUID.randomUUID().toString();
session.setAttribute(ID, sessionId);
session.setAttribute(LAST_ACTIVITY, System.currentTimeMillis());
session.setSessionTimeout(DEFAULT_TIMEOUT_SEC);
return session;
}
public static AuthSession toAuthSession(Map<String, Object> attribs) {
AuthSession session = new AuthSession();
for (Object key : attribs.keySet()) {
session.attribs.put(key.toString(), attribs.get(key));
}
return session;
}
// Normalized attributes for data storage.
private final ConcurrentMap<String, Object> attribs;
private AuthSession() {
this.attribs = new ConcurrentHashMap<String, Object>();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AuthSession)) {
return false;
}
AuthSession e = (AuthSession) obj;
return this.attribs.equals(e.attribs);
}
// private only used for storage
private Object getAttribute(String name) {
return attribs.get(name);
}
/**
* @return the flow in progress
*/
public ResponseType getAuthFlow() {
String name = (String) getAttribute(FLOW);
if (name == null) {
return null;
}
return ResponseType.valueOf(name);
}
/**
* @return return the client that established the cookie
*/
public String getClientId() {
return (String) getAttribute(CLIENT_ID);
}
public String[] getGrantedScope() {
@SuppressWarnings("unchecked")
List<String> list = (List<String>) getAttribute(GRANTED_SCOPE);
if (list == null) {
return null;
}
return (String[]) list.toArray(new String[list.size()]);
}
public String getId() {
return (String) getAttribute(ID);
}
/**
*
* @return the URL used in the initial authorization call
*/
public RedirectionURI getRedirectionURI() {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) getAttribute(CALLBACK);
if (map == null) {
return null;
}
String uri = map.get("uri").toString();
Boolean dynamic = (Boolean) map.get("dynamic");
return new RedirectionURI(uri, dynamic);
}
/**
*
* @return array of requested scopes
*/
public String[] getRequestedScope() {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) getAttribute(REQ_SCOPE);
if (list == null) {
return null;
}
return (String[]) list.toArray(new String[list.size()]);
}
/**
*
* @return identity of the authenticated user.
*/
public String getScopeOwner() {
return (String) getAttribute(OWNER);
}
/**
* Setting only affects new or updated sessions.
*
* @return current session timeout
*/
public int getSessionTimeout() {
return ((Number) getAttribute(TIMEOUT_SEC)).intValue();
}
/**
* @return client oauth state parameter
*/
public String getState() {
return (String) getAttribute(STATE);
}
private Object removeAttribute(String name) {
return attribs.remove(name);
}
/**
* Store attribute for internal use. The value must be normalized.
*
* @param name
* @param value
* normalized value.
*/
private void setAttribute(String name, Object value) {
if (value == null) {
removeAttribute(name);
} else {
attribs.put(name, value);
}
}
/**
* @param flow
* current executing flow
*/
public void setAuthFlow(ResponseType flow) {
// Normalize
setAttribute(FLOW, flow.name());
}
/**
* Set the client/application that created the cookie
*
* @param clientId
* POJO representing a client_id/secret
*/
public void setClientId(String clientId) {
setAttribute(CLIENT_ID, clientId);
}
public void setGrantedScope(String[] scope) {
setAttribute(GRANTED_SCOPE, Arrays.asList(scope));
}
public void setRedirectionURI(RedirectionURI uri) {
// Normalize
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("uri", uri.getURI());
map.put("dynamic", uri.isDynamicConfigured());
setAttribute(CALLBACK, map);
}
/**
* @param scope
* array of scopes requested but not yet approved
*/
public void setRequestedScope(String[] scope) {
setAttribute(REQ_SCOPE, Arrays.asList(scope));
}
/**
*
* @param owner
* the identity of the user of this session (openid)
*/
public void setScopeOwner(String owner) {
setAttribute(OWNER, owner);
}
/**
* Default is 600 sec = 10min
*
* @param timeSeconds
* sets the session expiry time in seconds
*/
public void setSessionTimeout(int timeSeconds) {
setAttribute(TIMEOUT_SEC, timeSeconds);
}
/**
* @param state
* to be save and returned with code
*/
public void setState(String state) {
setAttribute(STATE, state);
}
/**
* Get the Map interface that suitable for the database.
*
* @return
*/
public Map<String, Object> toMap() {
return attribs;
}
public void updateActivity() throws AuthSessionTimeoutException {
long currentTime = System.currentTimeMillis();
long lastActivity = ((Number) getAttribute(LAST_ACTIVITY)).longValue();
long delta = currentTime - lastActivity;
if ((delta / 1000) >= getSessionTimeout()) {
throw new AuthSessionTimeoutException();
}
lastActivity = System.currentTimeMillis();
setAttribute(LAST_ACTIVITY, lastActivity);
}
}