package controllers; import com.fasterxml.jackson.databind.JsonNode; import com.github.fge.jsonschema.core.report.ProcessingReport; import helpers.Countries; import helpers.JSONForm; import helpers.JsonLdConstants; import models.Resource; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.mail.DefaultAuthenticator; import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.SimpleEmail; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import play.Configuration; import play.Environment; import play.Logger; import play.mvc.Result; import javax.inject.Inject; import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Collectors; import java.util.stream.StreamSupport; public class UserIndex extends OERWorldMap { private File mProfiles; @Inject public UserIndex(Configuration aConf, Environment aEnv) { super(aConf, aEnv); mProfiles = new File(mConf.getString("user.profile.dir")); } public Result signup() { Map<String, Object> scope = new HashMap<>(); scope.put("countries", Countries.list(getLocale())); return ok(render("Registration", "UserIndex/register.mustache", scope)); } public Result register() { Resource user = Resource.fromJson(JSONForm.parseFormData(ctx().request().body().asFormUrlEncoded())); user.put(JsonLdConstants.CONTEXT, mConf.getString("jsonld.context")); String username = user.getAsString("email"); String password = user.getAsString("password"); String confirm = user.getAsString("password-confirm"); boolean emailToProfile = user.getAsString("add-email") != null; user.remove("password"); user.remove("password-confirm"); user.remove("add-email"); ProcessingReport processingReport = user.validate(); Result result; if (StringUtils.isEmpty(username)) { result = badRequest("No email address provided."); } else if (StringUtils.isEmpty(password)) { result = badRequest("No password provided."); } else if (!password.equals(confirm)) { result = badRequest("Passwords must match."); } else if (password.length() < 8) { result = badRequest("Password must be at least 8 characters long."); } else if (!processingReport.isSuccess()) { result = badRequest(processingReport.toString()); } else { String token = mAccountService.addUser(username, password); if (token == null) { result = badRequest("Failed to add " . concat(username)); } else { user.put("add-email", emailToProfile); try { saveProfile(token, user); } catch (IOException e) { Logger.error("Could not save profile", e); return badRequest("An error occurred"); } sendMail(username, MessageFormat.format(getEmails().getString("account.verify.message"), mConf.getString("proxy.host").concat(routes.UserIndex.verify(token).url())), getEmails().getString("account.verify.subject")); Map<String, Object> scope = new HashMap<>(); scope.put("username", username); result = ok(render("Successfully registered", "UserIndex/registered.mustache", scope)); } } return result; } public Result verify(String token) throws IOException { Result result; if (token == null) { result = badRequest("No token given."); } else { String username = mAccountService.verifyToken(token); if (username != null) { saveProfileToDb(token); Map<String, Object> scope = new HashMap<>(); scope.put("username", username); String userId = mAccountService.getProfileId(username); scope.put("id", userId); String profileUrl = mConf.getString("proxy.host").concat( routes.ResourceIndex.readDefault(userId, "HEAD").url()); scope.put("url", profileUrl); result = ok(render("User verified", "UserIndex/verified.mustache", scope)); } else { result = badRequest("Invalid token ".concat(token)); } } return result; } public Result requestPassword() { return ok(render("Reset Password", "UserIndex/password.mustache")); } public Result sendPassword() { Result result; Resource user = Resource.fromJson(JSONForm.parseFormData(ctx().request().body().asFormUrlEncoded())); String username; if (getHttpBasicAuthUser() != null) { username = getHttpBasicAuthUser(); String password = user.getAsString("password"); String updated = user.getAsString("password-new"); String confirm = user.getAsString("password-confirm"); if (StringUtils.isEmpty(password) || StringUtils.isEmpty(updated) || StringUtils.isEmpty(confirm)) { result = badRequest("Please fill out the form."); } else if (!updated.equals(confirm)) { result = badRequest("Passwords must match."); } else if (password.length() < 8) { result = badRequest("Password must be at least 8 characters long."); } else if (!mAccountService.updatePassword(username, password, updated)) { result = badRequest("Failed to update password for ".concat(username)); } else { result = ok(render("Password changed", "UserIndex/passwordChanged.mustache")); } } else { username = user.getAsString("email"); if (StringUtils.isEmpty(username) || !mAccountService.userExists(username)) { result = badRequest("No valid username provided."); } else { String password = new BigInteger(130, new SecureRandom()).toString(32); if (mAccountService.setPassword(username, password)) { sendMail(username, MessageFormat.format(getEmails().getString("account.password.message"), password), getEmails().getString("account.password.subject")); result = ok(render("Password reset", "UserIndex/passwordReset.mustache")); } else { result = badRequest("Failed to reset password."); } } } return result; } public Result newsletterSignup() { Map<String, Object> scope = new HashMap<>(); scope.put("countries", Countries.list(getLocale())); return ok(render("Registration", "UserIndex/newsletter.mustache", scope)); } public Result newsletterRegister() { Resource user = Resource.fromJson(JSONForm.parseFormData(ctx().request().body().asFormUrlEncoded())); if (!user.validate().isSuccess()) { return badRequest("Please provide a valid email address and select a country."); } String username = user.getAsString("email"); if (StringUtils.isEmpty(username)) { return badRequest("Not username given."); } String mailmanHost = mConf.getString("mailman.host"); String mailmanList = mConf.getString("mailman.list"); if (mailmanHost.isEmpty() || mailmanList.isEmpty()) { Logger.warn("No mailman configured, user ".concat(username) .concat(" not signed up for newsletter")); return internalServerError("Newsletter currently not available."); } HttpClient client = new DefaultHttpClient(); HttpPost request = new HttpPost("https://" + mailmanHost + "/mailman/subscribe/" + mailmanList); try { List<NameValuePair> nameValuePairs = new ArrayList<>(1); nameValuePairs.add(new BasicNameValuePair("email", user.get("email").toString())); request.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse response = client.execute(request); Integer responseCode = response.getStatusLine().getStatusCode(); if (!responseCode.equals(200)) { throw new IOException(response.getStatusLine().toString()); } } catch (IOException e) { Logger.error("Could not connect to mailman", e); return internalServerError(); } return ok(username + " signed up for newsletter."); } public Result editGroups() { Map<String, Map<String, Boolean>> groups = new HashMap<>(); for (String group : mAccountService.getGroups()) { Map<String, Boolean> users = new HashMap<>(); for (String user: mAccountService.getUsers()) { users.put(user, mAccountService.getUsers(group).contains(user)); } groups.put(group, users); } Map<String, Object> scope = new HashMap<>(); scope.put("groups", groups); return ok(render("Edit Groups", "UserIndex/groups.mustache", scope)); } public Result setGroups() { Map<String, List<String>> groupUsers = new HashMap<>(); if (ctx().request().body().asFormUrlEncoded() != null) { JsonNode jsonNode = JSONForm.parseFormData(ctx().request().body().asFormUrlEncoded()); Iterator<String> groupNames = jsonNode.fieldNames(); while (groupNames.hasNext()) { String group = groupNames.next(); List<String> users = StreamSupport.stream( Spliterators.spliteratorUnknownSize(jsonNode.get(group).fieldNames(), Spliterator.ORDERED), false).collect( Collectors.<String>toList()); groupUsers.put(group, users); } } if (mAccountService.setGroups(groupUsers)) { return ok(render("Groups Updated", "UserIndex/groupsChanged.mustache")); } else { return internalServerError("Failed to update groups"); } } private void sendMail(String aEmailAddress, String aMessage, String aSubject) { Email mail = new SimpleEmail(); try { mail.setMsg(aMessage); mail.setHostName(mConf.getString("mail.smtp.host")); mail.setSmtpPort(mConf.getInt("mail.smtp.port")); String smtpUser = mConf.getString("mail.smtp.user"); String smtpPass = mConf.getString("mail.smtp.password"); if (!smtpUser.isEmpty()) { mail.setAuthenticator(new DefaultAuthenticator(smtpUser, smtpPass)); } mail.setSSLOnConnect(mConf.getBoolean("mail.smtp.ssl")); mail.setStartTLSEnabled(mConf.getBoolean("mail.smtp.tls")); mail.setFrom(mConf.getString("mail.smtp.from"), mConf.getString("mail.smtp.sender")); mail.setSubject(aSubject); mail.addTo(aEmailAddress); mail.send(); Logger.info("Sent\n" + aMessage + "\nto " + aEmailAddress); } catch (EmailException e) { Logger.error("Failed to send\n" + aMessage + "\nto " + aEmailAddress, e); } } private void saveProfile(String aToken, Resource aPerson) throws IOException { FileUtils.writeStringToFile(new File(mProfiles, aToken), aPerson.toString(), StandardCharsets.UTF_8); } private void saveProfileToDb(String aToken) throws IOException { File profile = new File(mProfiles, aToken); Resource person = Resource.fromJson(FileUtils.readFileToString(profile, StandardCharsets.UTF_8)); if (person == null || !profile.delete()) { throw new IOException("Could not process profile for " + aToken); } String username = person.getAsString("email"); boolean addEmailToProfile = (boolean) person.get("add-email"); person.remove("add-email"); if (!addEmailToProfile) { person.remove("email"); } mBaseRepository.addResource(person, getMetadata()); mAccountService.setPermissions(person.getId(), username); mAccountService.setProfileId(username, person.getId()); } }