Skip to content

Commit

Permalink
refactoring: authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
witek committed Jul 16, 2015
1 parent 44ce10e commit 865207a
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 91 deletions.
32 changes: 17 additions & 15 deletions src/main/java/scrum/server/ScrumServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
/*
* Copyright 2011 Witoslaw Koczewsi <[email protected]>, Artjom Kochtchi
*
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero
* General Public License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
*
* You should have received a copy of the GNU General Public License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
package scrum.server;

import ilarkesto.auth.Auth;
import ilarkesto.auth.AuthenticationFailedException;
import ilarkesto.auth.WrongPasswordException;
import ilarkesto.auth.WrongPasswordInputException;
import ilarkesto.base.PermissionDeniedException;
import ilarkesto.base.Reflect;
import ilarkesto.base.Utl;
import ilarkesto.core.base.Str;
import ilarkesto.core.base.UserInputException;
import ilarkesto.core.persistance.EntityDoesNotExistException;
import ilarkesto.core.scope.In;
import ilarkesto.core.time.Date;
import ilarkesto.core.time.DateAndTime;
import ilarkesto.gwt.client.ErrorWrapper;
import ilarkesto.integration.ldap.Ldap;
import ilarkesto.persistence.ADao;
import ilarkesto.persistence.AEntity;
Expand All @@ -39,6 +40,7 @@

import scrum.client.DataTransferObject;
import scrum.client.admin.SystemMessage;
import scrum.server.admin.KunagiAuthenticationContext;
import scrum.server.admin.ProjectUserConfig;
import scrum.server.admin.SystemConfig;
import scrum.server.admin.User;
Expand Down Expand Up @@ -255,7 +257,7 @@ public void onPullStoryToSprint(GwtConversation conversation, String storyId) {
sprint.pullStory(story, currentUser);

postProjectEvent(conversation, currentUser.getName() + " pulled " + story.getReferenceAndLabel()
+ " to current sprint", story);
+ " to current sprint", story);

sendToClients(conversation, sprint);
sendToClients(conversation, story);
Expand All @@ -272,7 +274,7 @@ public void onKickStoryFromSprint(GwtConversation conversation, String storyId)
sprint.kickRequirement(story, currentUser);

postProjectEvent(conversation, currentUser.getName() + " kicked " + story.getReferenceAndLabel()
+ " from current sprint", story);
+ " from current sprint", story);

sendToClients(conversation, story.getTasksInSprint());
sendToClients(conversation, story);
Expand Down Expand Up @@ -338,18 +340,18 @@ public void onResetPassword(GwtConversation conversation, String userId) {
if (webApplication.getSystemConfig().isSmtpServerSet() && user.isEmailSet() && user.isEmailVerified()) {
user.triggerPasswordReset();
} else {
user.setPassword(webApplication.getSystemConfig().getDefaultUserPassword());
Auth.resetPasswordToDefault(user, new KunagiAuthenticationContext());
}
}

@Override
public void onChangePassword(GwtConversation conversation, String oldPassword, String newPassword) {
User user = conversation.getSession().getUser();
if (!user.matchesPassword(oldPassword)) throw new WrongPasswordException();

user.setPassword(newPassword);

log.info("password changed by", user);
try {
Auth.changePasswordWithCheck(oldPassword, newPassword, user, new KunagiAuthenticationContext());
} catch (UserInputException ex) {
conversation.getNextData().addError(ErrorWrapper.createUserInput(ex.getMessage()));
}
}

private void onEntityCreatedOnClient(GwtConversation conversation, AEntity entity, Map<String, String> properties) {
Expand Down Expand Up @@ -826,7 +828,7 @@ private void onRequirementChanged(GwtConversation conversation, Requirement requ
} else {
postProjectEvent(conversation,
currentUser.getName() + " kicked " + requirement.getReferenceAndLabel()
+ " from current sprint", requirement);
+ " from current sprint", requirement);
subscriptionService.notifySubscribers(requirement, "Story kicked from current Sprint",
conversation.getProject(), null);
}
Expand Down Expand Up @@ -1128,7 +1130,7 @@ public void onTestLdap(GwtConversation conversation) {
try {
Ldap.authenticateUserGetEmail(config.getLdapUrl(), config.getLdapUser(), config.getLdapPassword(),
config.getLdapBaseDn(), config.getLdapUserFilterRegex(), "dummyUser", "dummyPassword");
} catch (AuthenticationFailedException ex) {}
} catch (WrongPasswordInputException ex) {}
}

@Override
Expand Down
20 changes: 11 additions & 9 deletions src/main/java/scrum/server/ScrumWebApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package scrum.server;

import ilarkesto.auth.Auth;
import ilarkesto.auth.OpenId;
import ilarkesto.base.Sys;
import ilarkesto.base.Tm;
Expand Down Expand Up @@ -51,6 +52,7 @@
import scrum.server.admin.DeleteDisabledUsersTask;
import scrum.server.admin.DisableInactiveUsersTask;
import scrum.server.admin.DisableUsersWithUnverifiedEmailsTask;
import scrum.server.admin.KunagiAuthenticationContext;
import scrum.server.admin.ProjectUserConfig;
import scrum.server.admin.SystemConfig;
import scrum.server.admin.User;
Expand Down Expand Up @@ -178,10 +180,9 @@ protected void ensureIntegrity() {
if (!Str.isBlank(url)) getSystemConfig().setUrl(url);

if (getUserDao().getEntities().isEmpty()) {
String password = getSystemConfig().getDefaultUserPassword();
log.warn("No users. Creating initial user <admin> with password <" + password + ">");
User admin = getUserDao().postUserWithDefaultPassword("admin");
admin.setPassword(password);
log.warn("No users. Creating initial user <admin> with default password <"
+ getSystemConfig().getDefaultUserPassword() + ">");
User admin = getUserDao().postUser("admin");
admin.setAdmin(true);
}

Expand Down Expand Up @@ -244,10 +245,10 @@ public String createUrl(Project project, AEntity entity) {
private void createTestData() {
log.warn("Creating test data");

getUserDao().postUserWithDefaultPassword("homer");
getUserDao().postUserWithDefaultPassword("cartman");
getUserDao().postUserWithDefaultPassword("duke");
getUserDao().postUserWithDefaultPassword("spinne");
getUserDao().postUser("homer");
getUserDao().postUser("cartman");
getUserDao().postUser("duke");
getUserDao().postUser("spinne");

getProjectDao().postExampleProject(getUserDao().getUserByName("admin"), getUserDao().getUserByName("cartman"),
getUserDao().getUserByName("admin"));
Expand Down Expand Up @@ -320,7 +321,8 @@ private String getBaseUrl() {
public boolean isAdminPasswordDefault() {
User admin = getUserDao().getUserByName("admin");
if (admin == null) return false;
return admin.matchesPassword(getSystemConfig().getDefaultUserPassword());

return Auth.isPasswordMatchingDefaultPassword(admin, new KunagiAuthenticationContext());
}

public synchronized void postProjectEvent(Project project, String message, AEntity subject) {
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/scrum/server/admin/KunagiAuthenticationContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2011 Witoslaw Koczewsi <[email protected]>
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero
* General Public License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not,
* see <http://www.gnu.org/licenses/>.
*/
package scrum.server.admin;

import ilarkesto.auth.AuthenticationContext;
import ilarkesto.core.base.Utl;
import ilarkesto.core.logging.Log;

import java.util.Set;

import scrum.server.ScrumWebApplication;

public class KunagiAuthenticationContext implements AuthenticationContext<User> {

private static final Log log = Log.get(KunagiAuthenticationContext.class);

@Override
public String getPasswordHash(User user) {
return user.getPassword();
}

@Override
public String getPasswordSalt(User user) {
return user.getPasswordSalt();
}

@Override
public void setPasswordSalt(User user, String passwordSalt) {
user.setPasswordSalt(passwordSalt);
}

@Override
public void setPasswordHash(User user, String passwordHash) {
user.setPassword(passwordHash);
}

@Override
public void passwordReset(User user) {
log.info("Password reset:", user);
}

@Override
public void passwordChanged(User user) {
log.info("Password changed:", user);
}

@Override
public Set<String> getUsersKnownStrings(User user) {
return Utl.toSet(user.getName(), user.getFullName(), user.getEmail());
}

@Override
public String getDefaultPassword(User user) {
return ScrumWebApplication.get().getSystemConfig().getDefaultUserPassword();
}

@Override
public String getNewPasswordVeto(User user, String password) {
return null;
}

}
13 changes: 11 additions & 2 deletions src/main/java/scrum/server/admin/LoginServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
*/
package scrum.server.admin;

import ilarkesto.auth.Auth;
import ilarkesto.auth.AuthenticationFailedException;
import ilarkesto.auth.OpenId;
import ilarkesto.base.Str;
import ilarkesto.base.Utl;
import ilarkesto.core.base.UserInputException;
import ilarkesto.core.logging.Log;
import ilarkesto.core.time.DateAndTime;
import ilarkesto.integration.ldap.Ldap;
Expand Down Expand Up @@ -177,7 +179,14 @@ private void createAccount(String username, String email, String password, Strin
return;
}

User user = userDao.postUser(email, username, password);
User user;
try {
user = userDao.postUser(email, username, password);
} catch (UserInputException ex) {
renderLoginPage(req, username, email, historyToken,
"Creating account failed. Password rejected: " + ex.getMessage() + ".", false, true);
return;
}
user.setLastLoginDateAndTime(DateAndTime.now());
user.triggerEmailVerification();
webApplication.triggerRegisterNotification(user, req.getRemoteHost());
Expand Down Expand Up @@ -354,7 +363,7 @@ private void login(String username, String password, boolean keepmeloggedin, Str
}
} else {
// default password authentication
authenticated = user != null && user.matchesPassword(password);
authenticated = user != null && Auth.isPasswordMatching(password, user, new KunagiAuthenticationContext());
}

if (!authenticated || user == null) {
Expand Down
55 changes: 14 additions & 41 deletions src/main/java/scrum/server/admin/User.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
/*
* Copyright 2011 Witoslaw Koczewsi <[email protected]>, Artjom Kochtchi
*
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero
* General Public License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
*
* You should have received a copy of the GNU General Public License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
package scrum.server.admin;

import ilarkesto.auth.Auth;
import ilarkesto.auth.AuthUser;
import ilarkesto.auth.PasswordHasher;
import ilarkesto.base.CryptOneWay;
import ilarkesto.base.Str;
import ilarkesto.base.Utl;
import ilarkesto.core.base.Uuid;
import ilarkesto.core.base.UserInputException;
import ilarkesto.core.logging.Log;
import ilarkesto.integration.gravatar.Gravatar;
import ilarkesto.integration.gravatar.Profile;
Expand Down Expand Up @@ -92,13 +91,13 @@ public void triggerEmailVerification() {
StringBuilder sb = new StringBuilder();
sb.append(
"You have created an account for " + webApplication.getSystemConfig().getInstanceNameWithApplicationLabel())
.append(urlBase).append("\n");
.append(urlBase).append("\n");
sb.append("\n");
sb.append("Please visit the following link, to confirm your email: ").append(urlBase)
.append("confirmEmail?user=").append(getId()).append("&email=").append(getEmail()).append("\n");
.append("confirmEmail?user=").append(getId()).append("&email=").append(getEmail()).append("\n");
sb.append("\n");
sb.append("Please confirm your email within " + HOURS_FOR_EMAIL_VERIFICATION
+ " hours, otherwise your account will be deleted.\n");
+ " hours, otherwise your account will be deleted.\n");
try {
emailSender.sendEmail((String) null, getEmail(), "Kunagi email verification: " + getEmail(), sb.toString());
} catch (Exception ex) {
Expand All @@ -121,8 +120,11 @@ private void sendPasswordMail(String reason) {
}

String newPassword = Str.generatePassword(8);
setPassword(newPassword);
log.info("Password changed for", this);
try {
Auth.setPassword(newPassword, this, new KunagiAuthenticationContext());
} catch (UserInputException ex) {
throw new RuntimeException(ex);
}

StringBuilder sb = new StringBuilder();
sb.append(reason + " for your Kunagi account on ").append(webApplication.createUrl(null)).append("\n");
Expand All @@ -140,34 +142,6 @@ public String getRealName() {
return getName();
}

@Override
public boolean matchesPassword(String passwordToCheck) {
if (Str.isBlank(passwordToCheck)) return false;
if (!isPasswordSet()) return false;

String password = getPassword();

// legacy check
if (password.startsWith(CryptOneWay.DEFAULT_SALT)) {
boolean success = CryptOneWay.cryptWebPassword(passwordToCheck).equals(password);
if (!success) return false;
log.warn("Converting old password hash into new:", this);
setPassword(passwordToCheck);
return true;
}

return hashPassword(passwordToCheck).equals(password);
}

@Override
protected String preparePassword(String password) {
return super.preparePassword(hashPassword(password));
}

private String hashPassword(String password) {
return PasswordHasher.hashPassword(password, getPasswordSalt(), "SHA-256:");
}

@Override
public String getAutoLoginString() {
return getLoginToken();
Expand All @@ -181,8 +155,7 @@ public void createLoginToken() {
public void onEnsureIntegrity() {
super.onEnsureIntegrity();

if (!isPasswordSaltSet()) setPasswordSalt(Uuid.create());
if (!isPasswordSet()) setPassword(webApplication.getSystemConfig().getDefaultUserPassword());
if (!isPasswordSet()) Auth.resetPasswordToDefault(this, new KunagiAuthenticationContext());

if (!isPublicNameSet()) setPublicName(getName());
if (!isColorSet()) setColor(getDefaultColor());
Expand Down
Loading

0 comments on commit 865207a

Please sign in to comment.