diff options
Diffstat (limited to 'src/com/p4square/grow/frontend')
14 files changed, 618 insertions, 76 deletions
diff --git a/src/com/p4square/grow/frontend/ErrorPage.java b/src/com/p4square/grow/frontend/ErrorPage.java new file mode 100644 index 0000000..4a9380f --- /dev/null +++ b/src/com/p4square/grow/frontend/ErrorPage.java @@ -0,0 +1,25 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend; + +import org.restlet.representation.StringRepresentation; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class ErrorPage extends StringRepresentation { + public static final ErrorPage TEMPLATE_NOT_FOUND = new ErrorPage(); + public static final ErrorPage RENDER_ERROR = new ErrorPage(); + + + public ErrorPage() { + super("TODO"); + } + + public ErrorPage(String s) { + super(s); + } +} diff --git a/src/com/p4square/grow/frontend/GrowFrontend.java b/src/com/p4square/grow/frontend/GrowFrontend.java index 5c49fe2..36e7544 100644 --- a/src/com/p4square/grow/frontend/GrowFrontend.java +++ b/src/com/p4square/grow/frontend/GrowFrontend.java @@ -7,20 +7,32 @@ package com.p4square.grow.frontend; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.UUID; + import org.restlet.Application; import org.restlet.Component; +import org.restlet.Client; +import org.restlet.Context; import org.restlet.Restlet; import org.restlet.data.Protocol; import org.restlet.resource.Directory; import org.restlet.routing.Router; +import org.restlet.security.Authenticator; import org.apache.log4j.Logger; -import net.jesterpm.fmfacade.FMFacade; -import net.jesterpm.fmfacade.FreeMarkerPageResource; +import com.p4square.fmfacade.FMFacade; +import com.p4square.fmfacade.FreeMarkerPageResource; import com.p4square.grow.config.Config; +import com.p4square.f1oauth.F1OAuthHelper; +import com.p4square.f1oauth.SecondPartyVerifier; + +import com.p4square.grow.frontend.session.SessionCheckingAuthenticator; +import com.p4square.grow.frontend.session.SessionCreatingAuthenticator; + /** * This is the Restlet Application implementing the Grow project front-end. * It's implemented as an extension of FMFacade that connects interactive pages @@ -34,6 +46,8 @@ public class GrowFrontend extends FMFacade { private Config mConfig; + private F1OAuthHelper mHelper; + public GrowFrontend() { mConfig = new Config(); } @@ -62,14 +76,23 @@ public class GrowFrontend extends FMFacade { } } + F1OAuthHelper getHelper() { + if (mHelper == null) { + mHelper = new F1OAuthHelper(getContext(), mConfig.getString("f1ConsumerKey", ""), + mConfig.getString("f1ConsumerSecret", ""), + mConfig.getString("f1BaseUrl", "staging.fellowshiponeapi.com"), + mConfig.getString("f1ChurchCode", "pfseawa"), + F1OAuthHelper.UserType.WEBLINK); + } + + return mHelper; + } + @Override protected Router createRouter() { Router router = new Router(getContext()); - final String loginPage = getConfig().getString("dynamicRoot", "") + "/login.html"; - - final LoginAuthenticator defaultGuard = - new LoginAuthenticator(getContext(), true, loginPage); + final Authenticator defaultGuard = new SessionCheckingAuthenticator(getContext(), true); defaultGuard.setNext(FreeMarkerPageResource.class); router.attachDefault(defaultGuard); router.attach("/login.html", LoginPageResource.class); @@ -81,14 +104,36 @@ public class GrowFrontend extends FMFacade { accountRouter.attach("/training/{chapter}", TrainingPageResource.class); accountRouter.attach("/training", TrainingPageResource.class); - final LoginAuthenticator accountGuard = - new LoginAuthenticator(getContext(), false, loginPage); - accountGuard.setNext(accountRouter); + final Authenticator accountGuard = createAuthenticatorChain(accountRouter); router.attach("/account", accountGuard); return router; } + private Authenticator createAuthenticatorChain(Restlet last) { + final Context context = getContext(); + final String loginPage = getConfig().getString("dynamicRoot", "") + "/login.html"; + + // This is used to check for an existing session + SessionCheckingAuthenticator sessionChk = new SessionCheckingAuthenticator(context, true); + + // This is used to authenticate the user + SecondPartyVerifier f1Verifier = new SecondPartyVerifier(getHelper()); + LoginFormAuthenticator loginAuth = new LoginFormAuthenticator(context, false, f1Verifier); + loginAuth.setLoginFormUrl(loginPage); + loginAuth.setLoginPostUrl("/account/authenticate"); + + // This is used to create a new session for a newly authenticated user. + SessionCreatingAuthenticator sessionCreate = new SessionCreatingAuthenticator(context); + + sessionChk.setNext(loginAuth); + loginAuth.setNext(sessionCreate); + + sessionCreate.setNext(last); + + return sessionChk; + } + /** * Stand-alone main for testing. */ @@ -98,14 +143,16 @@ public class GrowFrontend extends FMFacade { component.getServers().add(Protocol.HTTP, 8085); component.getClients().add(Protocol.HTTP); component.getClients().add(Protocol.FILE); - + component.getClients().add(new Client(null, Arrays.asList(Protocol.HTTPS), "org.restlet.ext.httpclient.HttpClientHelper")); + // Static content try { component.getDefaultHost().attach("/images/", new FileServingApp("./build/images/")); component.getDefaultHost().attach("/scripts", new FileServingApp("./build/scripts")); component.getDefaultHost().attach("/style.css", new FileServingApp("./build/style.css")); + component.getDefaultHost().attach("/favicon.ico", new FileServingApp("./build/favicon.ico")); } catch (IOException e) { - cLog.error("Could not create directory for static resources: " + cLog.error("Could not create directory for static resources: " + e.getMessage(), e); } @@ -139,7 +186,7 @@ public class GrowFrontend extends FMFacade { cLog.fatal("Could not start: " + e.getMessage(), e); } } - + private static class FileServingApp extends Application { private final String mPath; diff --git a/src/com/p4square/grow/frontend/LoginFormAuthenticator.java b/src/com/p4square/grow/frontend/LoginFormAuthenticator.java new file mode 100644 index 0000000..d5a3c22 --- /dev/null +++ b/src/com/p4square/grow/frontend/LoginFormAuthenticator.java @@ -0,0 +1,122 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend; + +import org.apache.log4j.Logger; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Form; +import org.restlet.data.Reference; +import org.restlet.security.Authenticator; +import org.restlet.security.Verifier; + +/** + * LoginFormAuthenticator changes + * + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class LoginFormAuthenticator extends Authenticator { + private static final Logger LOG = Logger.getLogger(LoginFormAuthenticator.class); + + private final Verifier mVerifier; + + private String mLoginPage = "/login.html"; + private String mLoginPostUrl = "/authenticate"; + private String mDefaultRedirect = "/index.html"; + + public LoginFormAuthenticator(Context context, boolean optional, Verifier verifier) { + super(context, false, optional, null); + + mVerifier = verifier; + } + + public void setLoginFormUrl(String url) { + mLoginPage = url; + } + + public void setLoginPostUrl(String url) { + mLoginPostUrl = url; + } + + @Override + protected int beforeHandle(Request request, Response response) { + if (request.getClientInfo().isAuthenticated()) { + // TODO: Logout + LOG.debug("Already authenticated. Skipping"); + return CONTINUE; + + } else { + return super.beforeHandle(request, response); + } + } + + + @Override + protected boolean authenticate(Request request, Response response) { + String requestPath = request.getResourceRef().getPath(); + boolean isLoginAttempt = mLoginPostUrl.equals(requestPath); + + Form query = request.getOriginalRef().getQueryAsForm(); + String redirect = query.getFirstValue("redirect"); + if (redirect == null) { + if (isLoginAttempt) { + redirect = mDefaultRedirect; + } else { + redirect = request.getResourceRef().getRelativePart(); + } + } + + boolean authenticationFailed = false; + + if (isLoginAttempt) { + LOG.debug("Attempting authentication"); + + // Process login form + final Form form = new Form(request.getEntity()); + final String email = form.getFirstValue("email"); + final String password = form.getFirstValue("password"); + + boolean authenticated = false; + + if (email != null && !"".equals(email) && + password != null && !"".equals(password)) { + + LOG.debug("Got login request from " + email); + + request.setChallengeResponse( + new ChallengeResponse(ChallengeScheme.HTTP_BASIC, email, password.toCharArray())); + + // We expect the verifier to setup the User object. + int result = mVerifier.verify(request, response); + if (result == Verifier.RESULT_VALID) { + // TODO: Ensure redirect is a relative url. + response.redirectSeeOther(redirect); + return true; + } + } + + authenticationFailed = true; + } + + if (!isOptional() || authenticationFailed) { + Reference ref = new Reference(mLoginPage); + ref.addQueryParameter("redirect", redirect); + + if (authenticationFailed) { + ref.addQueryParameter("retry", "t"); + } + + LOG.debug("Redirecting to " + ref.toString()); + response.redirectSeeOther(ref.toString()); + } + LOG.debug("Failing authentication."); + return false; + } +} diff --git a/src/com/p4square/grow/frontend/LoginPageResource.java b/src/com/p4square/grow/frontend/LoginPageResource.java index 70caa3e..e645c1b 100644 --- a/src/com/p4square/grow/frontend/LoginPageResource.java +++ b/src/com/p4square/grow/frontend/LoginPageResource.java @@ -17,7 +17,7 @@ import org.restlet.ext.freemarker.TemplateRepresentation; import org.apache.log4j.Logger; -import net.jesterpm.fmfacade.FreeMarkerPageResource; +import com.p4square.fmfacade.FreeMarkerPageResource; /** * LoginPageResource presents a login page template and processes the response. @@ -57,7 +57,11 @@ public class LoginPageResource extends FreeMarkerPageResource { Map<String, Object> root = getRootObject(); - root.put("errorMessage", mErrorMessage); + Form query = getRequest().getOriginalRef().getQueryAsForm(); + String retry = query.getFirstValue("retry"); + if ("t".equals("retry")) { + root.put("errorMessage", "Invalid email or password."); + } return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); @@ -68,36 +72,4 @@ public class LoginPageResource extends FreeMarkerPageResource { } } - /** - * Process login and authenticate the user. - */ - @Override - protected Representation post(Representation entity) { - final Form form = new Form(entity); - final String email = form.getFirstValue("email"); - final String password = form.getFirstValue("password"); - - boolean authenticated = false; - - // TODO: Do something real here - if (email != null && !"".equals(email)) { - cLog.debug("Got login request from " + email); - - // TODO: Encrypt user info - getResponse().getCookieSettings().add(LoginAuthenticator.COOKIE_NAME, email); - - authenticated = true; - } - - if (authenticated) { - // TODO: Better return url. - getResponse().redirectSeeOther(mGrowFrontend.getConfig().getString("dynamicRoot", "") + "/index.html"); - return null; - - } else { - // Send them back to the login page... - mErrorMessage = "Incorrect Email or Password."; - return get(); - } - } } diff --git a/src/com/p4square/grow/frontend/NewAccountResource.java b/src/com/p4square/grow/frontend/NewAccountResource.java new file mode 100644 index 0000000..b72680a --- /dev/null +++ b/src/com/p4square/grow/frontend/NewAccountResource.java @@ -0,0 +1,115 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend; + +import java.util.Map; + +import freemarker.template.Template; + +import org.restlet.data.Form; +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.resource.ServerResource; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.ext.freemarker.TemplateRepresentation; + +import org.apache.log4j.Logger; + +import com.p4square.f1oauth.F1OAuthHelper; +import com.p4square.restlet.oauth.OAuthException; + +import com.p4square.fmfacade.FreeMarkerPageResource; + +/** + * This resource creates a new InFellowship account. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class NewAccountResource extends FreeMarkerPageResource { + private static Logger LOG = Logger.getLogger(NewAccountResource.class); + + private GrowFrontend mGrowFrontend; + private F1OAuthHelper mHelper; + + private String mErrorMessage; + + private String mLoginPageUrl; + private String mVerificationPage; + + @Override + public void doInit() { + super.doInit(); + + mGrowFrontend = (GrowFrontend) getApplication(); + mHelper = mGrowFrontend.getHelper(); + + mErrorMessage = null; + + mLoginPageUrl = ""; + mVerificationPage = ""; + } + + /** + * Return the login page. + */ + @Override + protected Representation get() { + Template t = mGrowFrontend.getTemplate("pages/newaccount.html.ftl"); + + try { + if (t == null) { + setStatus(Status.CLIENT_ERROR_NOT_FOUND); + return ErrorPage.TEMPLATE_NOT_FOUND; + } + + Map<String, Object> root = getRootObject(); + root.put("errorMessage", mErrorMessage); + + return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); + + } catch (Exception e) { + LOG.fatal("Could not render page: " + e.getMessage(), e); + setStatus(Status.SERVER_ERROR_INTERNAL); + return ErrorPage.RENDER_ERROR; + } + } + + @Override + protected Representation post(Representation rep) { + Form form = new Form(rep); + + String firstname = form.getFirstValue("firstname"); + String lastname = form.getFirstValue("lastname"); + String email = form.getFirstValue("email"); + + if (isEmpty(firstname)) { + mErrorMessage += "First Name is a required field. "; + } + if (isEmpty(lastname)) { + mErrorMessage += "Last Name is a required field. "; + } + if (isEmpty(email)) { + mErrorMessage += "Email is a required field. "; + } + + if (mErrorMessage.length() > 0) { + return get(); + } + + try { + mHelper.createAccount(firstname, lastname, email, mLoginPageUrl); + getResponse().redirectSeeOther(mVerificationPage); + return new StringRepresentation("Redirecting to " + mVerificationPage); + + } catch (OAuthException e) { + return new ErrorPage(e.getStatus().getDescription()); + } + } + + private boolean isEmpty(String s) { + return s != null && s.trim().length() > 0; + } +} diff --git a/src/com/p4square/grow/frontend/SurveyPageResource.java b/src/com/p4square/grow/frontend/SurveyPageResource.java index 351eade..8a3b5a5 100644 --- a/src/com/p4square/grow/frontend/SurveyPageResource.java +++ b/src/com/p4square/grow/frontend/SurveyPageResource.java @@ -18,10 +18,10 @@ import org.restlet.resource.ServerResource; import org.apache.log4j.Logger; -import net.jesterpm.fmfacade.json.JsonRequestClient; -import net.jesterpm.fmfacade.json.JsonResponse; +import com.p4square.fmfacade.json.JsonRequestClient; +import com.p4square.fmfacade.json.JsonResponse; -import net.jesterpm.fmfacade.FreeMarkerPageResource; +import com.p4square.fmfacade.FreeMarkerPageResource; import com.p4square.grow.config.Config; diff --git a/src/com/p4square/grow/frontend/TrainingPageResource.java b/src/com/p4square/grow/frontend/TrainingPageResource.java index 6c89ac9..459eb9a 100644 --- a/src/com/p4square/grow/frontend/TrainingPageResource.java +++ b/src/com/p4square/grow/frontend/TrainingPageResource.java @@ -20,10 +20,10 @@ import org.restlet.resource.ServerResource; import org.apache.log4j.Logger; -import net.jesterpm.fmfacade.json.JsonRequestClient; -import net.jesterpm.fmfacade.json.JsonResponse; +import com.p4square.fmfacade.json.JsonRequestClient; +import com.p4square.fmfacade.json.JsonResponse; -import net.jesterpm.fmfacade.FreeMarkerPageResource; +import com.p4square.fmfacade.FreeMarkerPageResource; import com.p4square.grow.config.Config; diff --git a/src/com/p4square/grow/frontend/VideosResource.java b/src/com/p4square/grow/frontend/VideosResource.java index fed315b..cdb2fb4 100644 --- a/src/com/p4square/grow/frontend/VideosResource.java +++ b/src/com/p4square/grow/frontend/VideosResource.java @@ -17,8 +17,8 @@ import org.restlet.resource.ServerResource; import org.apache.log4j.Logger; -import net.jesterpm.fmfacade.json.JsonRequestClient; -import net.jesterpm.fmfacade.json.JsonResponse; +import com.p4square.fmfacade.json.JsonRequestClient; +import com.p4square.fmfacade.json.JsonResponse; import com.p4square.grow.config.Config; diff --git a/src/com/p4square/grow/frontend/session/Session.java b/src/com/p4square/grow/frontend/session/Session.java new file mode 100644 index 0000000..3a241ef --- /dev/null +++ b/src/com/p4square/grow/frontend/session/Session.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend.session; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.restlet.security.User; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class Session { + private static final long LIFETIME = 86400; + + private final String mSessionId; + private final User mUser; + private final Map<String, String> mData; + private long mExpires; + + Session(User user) { + mUser = user; + mSessionId = UUID.randomUUID().toString(); + mExpires = System.currentTimeMillis() + LIFETIME; + mData = new HashMap<String, String>(); + } + + void touch() { + mExpires = System.currentTimeMillis() + LIFETIME; + } + + boolean isExpired() { + return System.currentTimeMillis() > mExpires; + } + + public String getId() { + return mSessionId; + } + + public String get(String key) { + return mData.get(key); + } + + public void put(String key, String value) { + mData.put(key, value); + } + + public User getUser() { + return mUser; + } +} diff --git a/src/com/p4square/grow/frontend/LoginAuthenticator.java b/src/com/p4square/grow/frontend/session/SessionAuthenticator.java index 64f5827..ac194af 100644 --- a/src/com/p4square/grow/frontend/LoginAuthenticator.java +++ b/src/com/p4square/grow/frontend/session/SessionAuthenticator.java @@ -2,9 +2,7 @@ * Copyright 2013 Jesse Morgan */ -package com.p4square.grow.frontend; - -import org.apache.log4j.Logger; +package com.p4square.grow.frontend.session; import org.restlet.Context; import org.restlet.Request; @@ -13,27 +11,12 @@ import org.restlet.security.Authenticator; import org.restlet.security.User; /** - * LoginAuthenticator decrypts a cookie containing the user's session info - * and makes that information available as the ClientInfo's User object. - * - * If this Authenticator is not optional, the user will be redirected to a - * login page. - * + * * @author Jesse Morgan <jesse@jesterpm.net> */ -public class LoginAuthenticator extends Authenticator { - private static Logger cLog = Logger.getLogger(LoginAuthenticator.class); - - public static final String COOKIE_NAME = "growsession"; - - private final String mLoginPage; - - public LoginAuthenticator(Context context, boolean optional, String loginPage) { - super(context, optional); - - mLoginPage = loginPage; - } - +public class SessionAuthenticator /*extends Authenticator*/ { + /* + @Override protected boolean authenticate(Request request, Response response) { // Check for authentication cookie final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); @@ -49,4 +32,5 @@ public class LoginAuthenticator extends Authenticator { response.redirectSeeOther(mLoginPage); return false; } + */ } diff --git a/src/com/p4square/grow/frontend/session/SessionCheckingAuthenticator.java b/src/com/p4square/grow/frontend/session/SessionCheckingAuthenticator.java new file mode 100644 index 0000000..8382aff --- /dev/null +++ b/src/com/p4square/grow/frontend/session/SessionCheckingAuthenticator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend.session; + +import org.apache.log4j.Logger; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.security.Authenticator; + +/** + * Authenticator which succeeds if a valid Session exists. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class SessionCheckingAuthenticator extends Authenticator { + private static final Logger LOG = Logger.getLogger(SessionCheckingAuthenticator.class); + + public SessionCheckingAuthenticator(Context context, boolean optional) { + super(context, optional); + } + + protected boolean authenticate(Request request, Response response) { + Session s = Sessions.getInstance().get(request); + + if (s != null) { + request.getClientInfo().setUser(s.getUser()); + return true; + + } else { + return false; + } + } + +} diff --git a/src/com/p4square/grow/frontend/session/SessionCookieAuthenticator.java b/src/com/p4square/grow/frontend/session/SessionCookieAuthenticator.java new file mode 100644 index 0000000..789f58e --- /dev/null +++ b/src/com/p4square/grow/frontend/session/SessionCookieAuthenticator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend.session; + +import org.apache.log4j.Logger; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.security.Authenticator; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class SessionCookieAuthenticator extends Authenticator { + private static final Logger LOG = Logger.getLogger(SessionCookieAuthenticator.class); + + private static final String COOKIE_NAME = "S"; + + private final Sessions mSessions; + + public SessionCookieAuthenticator(Context context, boolean optional, Sessions sessions) { + super(context, optional); + + mSessions = sessions; + } + + protected boolean authenticate(Request request, Response response) { + final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); + + if (request.getClientInfo().isAuthenticated()) { + // Request is already authenticated... create session if it doesn't exist. + if (cookie == null) { + Session s = mSessions.create(request.getClientInfo().getUser()); + response.getCookieSettings().add(COOKIE_NAME, s.getId()); + } + + return true; + + } else { + // Check for authentication cookie + if (cookie != null) { + LOG.debug("Got cookie: " + cookie); + + Session s = mSessions.get(cookie); + if (s != null) { + request.getClientInfo().setUser(s.getUser()); + return true; + } + } + + return false; + } + } + +} diff --git a/src/com/p4square/grow/frontend/session/SessionCreatingAuthenticator.java b/src/com/p4square/grow/frontend/session/SessionCreatingAuthenticator.java new file mode 100644 index 0000000..ce6024c --- /dev/null +++ b/src/com/p4square/grow/frontend/session/SessionCreatingAuthenticator.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend.session; + +import org.apache.log4j.Logger; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.security.Authenticator; +import org.restlet.security.User; + +/** + * Authenticator which creates a Session for the request and adds a cookie + * to the response. + * + * The Request MUST be Authenticated and MUST have a User object associated. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class SessionCreatingAuthenticator extends Authenticator { + private static final Logger LOG = Logger.getLogger(SessionCreatingAuthenticator.class); + + public SessionCreatingAuthenticator(Context context) { + super(context, true); + } + + protected boolean authenticate(Request request, Response response) { + if (Sessions.getInstance().get(request) != null) { + return true; + } + + User user = request.getClientInfo().getUser(); + + if (request.getClientInfo().isAuthenticated() && user != null) { + Sessions.getInstance().create(request, response); + return true; + } + + return false; + } + +} diff --git a/src/com/p4square/grow/frontend/session/Sessions.java b/src/com/p4square/grow/frontend/session/Sessions.java new file mode 100644 index 0000000..094d2f0 --- /dev/null +++ b/src/com/p4square/grow/frontend/session/Sessions.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.grow.frontend.session; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +import org.restlet.Response; +import org.restlet.Request; +import org.restlet.security.User; + +/** + * Singleton Session Manager. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class Sessions { + private static final String COOKIE_NAME = "S"; + + private static final Sessions THE = new Sessions(); + public static Sessions getInstance() { + return THE; + } + + private final Map<String, Session> mSessions; + + private Sessions() { + mSessions = new ConcurrentHashMap<String, Session>(); + } + + public Session get(String sessionid) { + Session s = mSessions.get(sessionid); + + if (s != null && !s.isExpired()) { + s.touch(); + return s; + } + + return null; + } + + /** + * Get the Session associated with the Request. + * @return A session or null if no session is found. + */ + public Session get(Request request) { + final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); + + if (cookie != null) { + return get(cookie); + } + + return null; + } + + public Session create(User user) { + if (user == null) { + throw new IllegalArgumentException("Can not create session for null user."); + } + + Session s = new Session(user); + mSessions.put(s.getId(), s); + + return s; + } + + /** + * Create a new Session and add the Session cookie to the response. + */ + public Session create(Request request, Response response) { + Session s = create(request.getClientInfo().getUser()); + + request.getCookies().add(COOKIE_NAME, s.getId()); + response.getCookieSettings().add(COOKIE_NAME, s.getId()); + + return s; + } +} |