package controllers;
import static play.data.Form.form;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Set;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import models.CacheableUser;
import models.GroupManager;
import models.User;
import models.User.Invite;
import models.User.Login;
import models.UserDAO;
import org.apache.jackrabbit.api.security.user.Group;
import org.jcrom.Jcrom;
import play.Logger;
import play.Play;
import play.data.Form;
import play.libs.F;
import play.mvc.Result;
import play.mvc.With;
import providers.CacheableUserProvider;
import providers.JackrabbitEmailPasswordAuthProvider;
import service.JcrSessionFactory;
import com.feth.play.module.pa.PlayAuthenticate;
import com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
public final class Application extends SessionAwareController {
@Inject
Application(
final JcrSessionFactory sessionFactory,
final Jcrom jcrom,
final CacheableUserProvider sessionHandler) {
super(sessionFactory, jcrom, sessionHandler);
}
@SubjectPresent
public final Result invite() {
return ok(views.html.Application.invite.render(form(Invite.class),
availableGroups())).as("text/html; charset=utf-8");
}
@SubjectPresent
public final Result postInvite() {
com.feth.play.module.pa.controllers.Authenticate.noCache(response());
final Form<Invite> filledForm = form(Invite.class).bindFromRequest();
if (filledForm.hasErrors()) {
// User did not fill everything properly
return badRequest(
views.html.Application.invite.render(filledForm, availableGroups()));
} else {
Result retval = UsernamePasswordAuthProvider.handleSignup(ctx());
final String[] groups =
ctx().request().body().asFormUrlEncoded().get("groups[]");
if (groups != null) {
// Ensure only allowed groups are used from the selected list
Set<String> assignableGroups =
Sets.intersection(
ImmutableSet.<String>copyOf(groups),
availableGroups());
assignUserGroups(filledForm.get().getEmail(), assignableGroups);
}
return retval;
}
}
@With(UncacheableAction.class)
public final Result login() {
return ok(views.html.Application.login.render(form(Login.class)));
}
public final Result postLogin() {
com.feth.play.module.pa.controllers.Authenticate.noCache(response());
final Form<Login> filledForm = form(Login.class).bindFromRequest();
if (filledForm.hasErrors()) {
Logger.debug(filledForm.errorsAsJson()+"");
// User did not fill everything properly
return badRequest(views.html.Application.login.render(filledForm));
} else {
// Everything was filled
return UsernamePasswordAuthProvider.handleLogin(ctx());
}
}
public final Result verify(final String email, final String token) {
return sessionFactory.inSession(new F.Function<Session, Result>() {
@Override
public Result apply(Session session) {
User user = getUserDAO(session).findByEmail(email);
if (user != null && user.checkVerificationToken(token)) {
return ok(views.html.Application.setPassword.render(
routes.Application.postVerify(email, token),
form(User.ChangePassword.class)));
} else {
return forbidden();
}
}
});
}
public final Result postVerify(final String email, final String token) {
return sessionFactory.inSession(new F.Function<Session, Result>() {
@Override
public Result apply(Session session) throws RepositoryException {
final UserDAO dao = getUserDAO(session);
final User user = dao.findByEmail(email);
if (user != null && user.checkVerificationToken(token)) {
Form<User.ChangePassword> filledForm =
form(User.ChangePassword.class).bindFromRequest();
if (filledForm.hasErrors()) {
return ok(views.html.Application.setPassword.render(
routes.Application.postVerify(email, token),
filledForm));
}
String clearPassword = filledForm.field("password").value();
dao.setPassword(user, clearPassword);
user.clearVerificationToken();
dao.update(user);
return PlayAuthenticate.loginAndRedirect(ctx(),
new JackrabbitEmailPasswordAuthProvider.LoginUser(
clearPassword, email));
} else {
return forbidden();
}
}
});
}
public final Result forgottenPassword() {
return ok(views.html.Application.forgottenPassword.render(
routes.Application.postForgottenPassword(),
form(User.ResetPassword.class)
)).as("text/html; charset=utf-8");
}
public final Result postForgottenPassword() {
com.feth.play.module.pa.controllers.Authenticate.noCache(response());
final Form<User.ResetPassword> filledForm =
form(User.ResetPassword.class).bindFromRequest();
if (filledForm.hasErrors()) {
// User did not fill everything properly
return badRequest(
views.html.Application.forgottenPassword.render(
routes.Application.postForgottenPassword(),
filledForm));
} else {
Play.application().plugin(JackrabbitEmailPasswordAuthProvider.class)
.sendResetEmail(ctx(), filledForm.field("email").value());
ctx().flash().put("info", "Reset email sent (if the account existed).");
return redirect(routes.Application.login());
}
}
@SubjectPresent
public final Result changePassword() {
return sessionFactory.inSession(new F.Function<Session, Result>() {
@Override
public Result apply(Session session) throws RepositoryException {
final Map<String, String[]> params =
ctx().request().body().asFormUrlEncoded();
final UserDAO dao = getUserDAO(session);
final User user = dao.get(getUser());
if (!params.containsKey("newPassword") ||
!params.containsKey("currentPassword")) {
return badRequest("Current and new password required.")
.as("text/plain");
}
final String currentPassword = params.get("currentPassword")[0];
final String newPassword = params.get("newPassword")[0];
if (dao.checkPassword(user, currentPassword)) {
dao.setPassword(user, newPassword);
return ok();
} else {
return badRequest("Incorrect current password.").as("text/plain");
}
}
});
}
public final Result oAuthDenied(String providerKey) {
// TODO: Implement
return ok();
}
@SubjectPresent
public final Result userExists(String encodedEmail) {
final String email = urlDecode(encodedEmail);
return sessionFactory.inSession(new F.Function<Session, Result>() {
@Override
public Result apply(Session session) throws Throwable {
final UserDAO dao = getUserDAO(session);
final User user = dao.findByEmail(email);
return ok(views.html.Application.userExists.render(user));
}
});
}
public final Result userUnverified(String encodedEmail) {
final String email = urlDecode(encodedEmail);
return sessionFactory.inSession(new F.Function<Session, Result>() {
@Override
public Result apply(Session session) throws Throwable {
final UserDAO dao = getUserDAO(session);
final User user = dao.findByEmail(email);
return ok(views.html.Application.userUnverified.render(user));
}
});
}
protected User assignUserGroups(final String email, final Set<String> groups){
return sessionFactory.inSession(new F.Function<Session, User>() {
@Override
public User apply(final Session session) throws RepositoryException {
final GroupManager groupManager = new GroupManager(session);
final UserDAO dao = getUserDAO(session);
final User newUser = dao.findByEmail(email);
for (String groupName : groups) {
final Group group = groupManager.find(groupName);
if (group == null) {
Logger.warn("Unable to resolve group ("+groupName+")");
continue;
}
try {
groupManager.addMember(groupName, newUser.getJackrabbitUserId());
Logger.info(
"Added new user ("+newUser+") to group ("+groupName+").");
} catch (PathNotFoundException e) {
Logger.warn(
"Unable to assign group ("+groupName+"): "+e.getMessage());
}
}
return newUser;
}
});
}
protected UserDAO getUserDAO(Session session) {
return new UserDAO(session, jcrom);
}
private String urlDecode(String str) {
try {
return URLDecoder.decode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
// Should never happen
throw new RuntimeException(e);
}
}
private Set<String> availableGroups() {
final CacheableUser user = getUser();
return inUserSession(new F.Function<Session, Set<String>>() {
@Override
public Set<String> apply(Session session) throws Throwable {
final ImmutableSet.Builder<String> l = ImmutableSet.builder();
final GroupManager gm = new GroupManager(session);
final Iterable<Group> availableGroups = user.hasRole("admin") ?
gm.list() :
gm.memberships(user.getJackrabbitUserId());
for (final Group g : availableGroups) {
l.add(g.getPrincipal().getName());
}
return l.build();
}
});
}
}