package providers; import static play.test.Helpers.fakeRequest; import static java.util.Collections.emptyMap; import static play.data.Form.form; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.jcr.Session; import org.jcrom.Jcrom; import models.User; import models.User.Invite; import models.User.Login; import models.UserDAO; import play.Application; import play.Logger; import play.Play; import play.api.http.MediaRange; import play.data.Form; import play.i18n.Lang; import play.libs.F; import play.libs.Scala; import play.mvc.Call; import play.mvc.Http; import play.mvc.Http.Context; import play.mvc.Http.Cookies; import play.mvc.Http.Request; import play.mvc.Http.RequestBody; import scala.collection.Seq; import service.GuiceInjectionPlugin; import service.JcrSessionFactory; import com.feth.play.module.mail.Mailer.Mail; import com.feth.play.module.mail.Mailer.Mail.Body; import com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider; import com.feth.play.module.pa.providers.password.UsernamePasswordAuthUser; import com.google.common.collect.ImmutableMap; public class JackrabbitEmailPasswordAuthProvider extends UsernamePasswordAuthProvider<String, JackrabbitEmailPasswordAuthProvider.LoginUser, JackrabbitEmailPasswordAuthProvider.SignupUser, Login, Invite> { private final Application application; public JackrabbitEmailPasswordAuthProvider(Application application) { super(application); this.application = application; } private JcrSessionFactory getSessionFactory() { return GuiceInjectionPlugin.getInjector(application).getInstance( JcrSessionFactory.class); } private Jcrom getJcrom() { return GuiceInjectionPlugin.getInjector(application).getInstance( Jcrom.class); } private UserDAO getUserDAO(Session session) { return new UserDAO(session, getJcrom()); } public static class SignupUser extends UsernamePasswordAuthUser { private static final long serialVersionUID = 1L; public final String name; public SignupUser(final String email, final String name) { super(null, email); this.name = name; } public User asNewUser() { User user = new User(); user.setEmail(getEmail()); user.setName(name); user.setVerified(false); return user; } } public static class LoginUser extends UsernamePasswordAuthUser { private static final long serialVersionUID = 1L; public LoginUser(final String clearPassword, final String email) { super(clearPassword, email); } @Override public String getId() { return getEmail(); } } public void signup(Invite invite) { Map<String, String> data = ImmutableMap.<String, String>builder() .put("email", invite.email) .put("name", invite.name) .build(); handleSignup(fakeContext(data)); } public void sendResetEmail(final Context ctx, final String email) { final SignupUser user = getSessionFactory().inSession( new F.Function<Session, SignupUser>() { @Override public SignupUser apply(final Session session) { final UserDAO dao = new UserDAO(session, getJcrom()); final User u = dao.findByEmail(email); return u!=null?new SignupUser(u.getEmail(), u.getName()):null; } }); if(user != null) { final String record = generateVerificationRecord(user); final Body body = getVerifyEmailMailingBody(record, user, ctx); final Mail verifyMail = new Mail("Password reset for AORRA", body, new String[] { getEmailName(user) }); mailer.sendMail(verifyMail); } } private Http.Context fakeContext(Map<String,String> data) { final play.api.mvc.Request<Http.RequestBody> req = fakeRequest().withFormUrlEncodedBody(data).getWrappedRequest(); Http.Request request = new Request() { @Override public RequestBody body() { return req.body(); } @Override public String uri() { return req.uri(); } @Override public String method() { return req.method(); } @Override public String version() { return req.version(); } @Override public String remoteAddress() { return req.remoteAddress(); } @Override public String host() { try { URL url = new URL(Play.application() .configuration().getString("application.baseUrl")); if (url.getPort() == -1 || url.getPort() == url.getDefaultPort()) { return url.getHost(); } else { return url.getHost() + ":" + url.getPort(); } } catch (MalformedURLException e) { throw new RuntimeException(e); } } @Override public String path() { return req.path(); } @Override public List<Lang> acceptLanguages() { List<Lang> list = new LinkedList<Lang>(); for (play.api.i18n.Lang l : Scala.asJava(req.acceptLanguages())) { list.add(new Lang(l)); } return list; } @Override @Deprecated public List<String> accept() { return Scala.asJava(req.accept()); } @Override public List<MediaRange> acceptedTypes() { return Scala.asJava(req.acceptedTypes()); } @Override public boolean accepts(String mimeType) { return req.accepts(mimeType); } @Override public Map<String, String[]> queryString() { return javaMap(req.queryString()); } @Override public Cookies cookies() { // Not implemented return null; } @Override public Map<String, String[]> headers() { return javaMap(req.headers().toMap()); } private Map<String, String[]> javaMap(scala.collection.Map<String, Seq<String>> map) { Map<String, String[]> m = new HashMap<String, String[]>(); for (Entry<String,Seq<String>> e : Scala.asJava(map).entrySet()) { m.put(e.getKey(), Scala.asJava(e.getValue()).toArray(new String[]{})); } return m; } }; Map<String,String> sessionData = emptyMap(); Map<String,String> flashData = emptyMap(); Map<String,Object> args = emptyMap(); return new Http.Context(req.id(), req, request, sessionData, flashData, args); } @Override protected String generateVerificationRecord(final SignupUser user) { if(user == null) { return null; } return getSessionFactory().inSession( new F.Function<Session, String>() { @Override public String apply(final Session session) { UserDAO dao = getUserDAO(session); User u = dao.findByEmail(user.getEmail()); if(u!=null) { try { return u.createVerificationToken(); } finally { dao.update(u); } } else { return null; } } }); } @Override protected String getVerifyEmailMailingSubject(SignupUser user, Context ctx) { return "Please verify your email address"; } @Override protected Body getVerifyEmailMailingBody(String verificationToken, SignupUser user, Context ctx) { return new Body(views.txt.email.verification.render( verificationToken, ctx.request(), user).toString()); } @Override protected LoginUser buildLoginAuthUser(Login login, Context ctx) { return new LoginUser(login.getPassword(), login.getEmail()); } @Override protected LoginUser transformAuthUser(SignupUser signupUser, Context ctx) { // This should never be called throw new UnsupportedOperationException("Signup users must validate"); } @Override protected SignupUser buildSignupAuthUser(Invite invite, Context ctx) { return new SignupUser(invite.email, invite.name); } @Override protected com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider.LoginResult loginUser(final LoginUser loginUser) { return getSessionFactory().inSession( new F.Function<Session, LoginResult>() { @Override public LoginResult apply(final Session session) throws Throwable { final UserDAO dao = getUserDAO(session); final User u = dao.findByEmail(loginUser.getEmail()); if (u == null) { Logger.debug(loginUser.getEmail() + " attempted to login but was not found."); return LoginResult.NOT_FOUND; } if (!u.isVerified()) { Logger.debug(u + " attempted to login but is still unverified."); return LoginResult.USER_UNVERIFIED; } if (!dao.checkPassword(u, loginUser.getPassword())) { Logger.debug(u + " provided an incorrect password."); return LoginResult.WRONG_PASSWORD; } Logger.debug(u + " successfully authenticated."); return LoginResult.USER_LOGGED_IN; } }); } @Override protected com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider.SignupResult signupUser(final SignupUser user) { final String e = user.getEmail(); return getSessionFactory().inSession( new F.Function<Session, SignupResult>() { @Override public SignupResult apply(final Session session) throws Throwable { final UserDAO dao = getUserDAO(session); { final User existingUser = dao.findByEmail(e); if (existingUser != null) { Logger.debug("Found existing user for "+e+": "+existingUser); if (existingUser.isVerified()) { return SignupResult.USER_EXISTS; } else { return SignupResult.USER_EXISTS_UNVERIFIED; } } } dao.create(user.asNewUser()); Logger.debug("Created new user: "+user.asNewUser()); return SignupResult.USER_CREATED_UNVERIFIED; } }); } @Override protected Form<Invite> getSignupForm() { return form(Invite.class); } @Override protected Form<Login> getLoginForm() { return form(Login.class); } @Override protected Call userExists(UsernamePasswordAuthUser authUser) { return controllers.routes.Application.userExists(authUser.getEmail()); } @Override protected Call userUnverified(UsernamePasswordAuthUser authUser) { return controllers.routes.Application.userUnverified(authUser.getEmail()); } @Override protected String onLoginUserNotFound(Context context) { context.flash().put("error", "Invalid email address or password."); return super.onLoginUserNotFound(context); } }