package com.project.website.shared.server.authentication;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletResponse;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.project.shared.server.ServerQueryString;
import com.project.shared.utils.StringUtils;
import com.project.website.shared.contracts.authentication.AuthenticationService;
import com.project.website.shared.data.Invitation;
import com.project.website.shared.data.QueryParameters;
import com.project.website.shared.data.User;
import com.project.website.shared.data.UserProfile;
public class AuthenticationServiceImpl extends RemoteServiceServlet implements AuthenticationService
{
private static final long serialVersionUID = 1L;
private static final String SITE_BASE_ADDR = "http://www.PopSimple.com";
private static final String INVITE_PATH = "/Login.html";
private static final String ADMIN_USERNAME = "admin@popsimple.com";
private static final String ADMIN_DEFAULT_PASSWORD = "admin";
@Override
protected void doUnexpectedFailure(Throwable e)
{
// see http://code.google.com/p/google-web-toolkit/issues/detail?id=3298
if (e instanceof IOException) {
// mask it.
return;
}
super.doUnexpectedFailure(e);
}
@Override
public void register(String email, String password, String name, Invitation invitation)
throws UserAlreadyExists
{
User authenticatedUser = getAuthenticatedUser();
boolean needsInvitation = false == canRegisterUsers(authenticatedUser);
boolean validInvitation = AuthenticationUtils.invitationIsValid(invitation);
if (needsInvitation && (false == validInvitation)) {
// User has no invitation and no privileges to register new users.
this.onInvalidInvitation();
return;
}
if (null != AuthenticationUtils.loadUser(email)) {
// todo do this normally
throw new UserAlreadyExists();
}
AuthenticationUtils.createUser(email, password, name);
if (needsInvitation || validInvitation) {
AuthenticationUtils.invalidateInvitation(invitation);
}
}
@Override
public void login(String username, String password)
{
this.logout();
if (Strings.isNullOrEmpty(username))
{
this.onAuthenticationFailed();
return;
}
this.validateAdminUserExists();
User user = AuthenticationUtils.loadUser(username);
if (null == user)
{
this.onAuthenticationFailed();
return;
}
if ((false == user.isEnabled) || (false == Objects.equal(user.password, AuthenticationUtils.hashPassword(password))))
{
this.onAuthenticationFailed();
return;
}
HttpAuthentication.setAuthCookies(user, this.getThreadLocalRequest(), this.getThreadLocalResponse());
}
private void validateAdminUserExists()
{
if (null != AuthenticationUtils.loadUser(ADMIN_USERNAME))
{
return;
}
AuthenticationUtils.createUser(ADMIN_USERNAME, ADMIN_DEFAULT_PASSWORD, "Admin");
}
private void onInvalidInvitation()
{
// TODO respond in a way that the client understands that this was the problem...
//onAuthenticationFailed();
throw new RuntimeException("Invitation was already used or is invalid.");
}
private void onAuthenticationFailed()
{
// There's a small GWT bug when using the thread local response:
// http://code.google.com/p/google-web-toolkit/issues/detail?id=3298
// it causes an exception (at least in Jetty) because it tries to write into the response after it has closed
// In any case it will get sent, so leaving it here.
try {
// TODO: use SC_FORBIDDEN instead?
this.getThreadLocalResponse().sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
// TODO Auto-generated catch block
// Mask this.
}
return;
}
@Override
public void logout() {
HttpAuthentication.clearAuthCookies(getThreadLocalRequest(), getThreadLocalResponse());
}
@Override
public void invite(String email, String message, String name)
{
// TODO add logic for validating the invited user
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
Invitation invitation = AuthenticationUtils.createInvitation();
ServerQueryString query = ServerQueryString.create();
query.set(QueryParameters.INVITE_ID, invitation.id);
String inviteUrl;
try {
URI inviteURI = new URI(SITE_BASE_ADDR + INVITE_PATH + "#" + query.toString());
inviteUrl = inviteURI.toURL().toString();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
try {
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("no-reply@popsimple.com", "PopSimple.com"));
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
msg.setSubject(name + " has invited you to PopSimple.com");
String messageWrapped = StringUtils.isWhitespaceOrNull(message) ? "" : ("Here's a message from " + name + ":\r\n"
+ "'" + message + "'\r\n");
String text = "Your invitation is waiting at " + inviteUrl + "\r\n"
+ messageWrapped + "\r\n"
+ "Come and try it out!";
msg.setText(text);
Transport.send(msg);
} catch (AddressException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
} catch (MessagingException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public UserProfile getUserProfile()
{
User user = getAuthenticatedUser();
boolean canInvite = this.canRegisterUsers(user);
UserProfile userProfile = new UserProfile(user.username, user.publicName, canInvite);
return userProfile;
}
private boolean canRegisterUsers(User user)
{
// TODO implement a permissions system
if (null == user) {
return false;
}
return Objects.equal(user.username, ADMIN_USERNAME);
}
private User getAuthenticatedUser()
{
User user = HttpAuthentication.getAuthenticatedUser(this.getThreadLocalRequest(), this.getThreadLocalResponse());
return user;
}
}