- User registration and password recover now work
- Improvements for ChallengeManager
- Groups and roles assigned to a user are now shown in in the user details view
- Groups assigned to a user can be edited


git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3981 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-04-08 17:54:17 +00:00
parent 49357d190a
commit 81fd867e31
29 changed files with 868 additions and 135 deletions

View File

@ -20,12 +20,12 @@ package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.FormSection;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
@ -49,16 +49,30 @@ import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.UncheckedWrapperException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.EmailAddress;
import org.libreccm.security.ChallengeManager;
import org.libreccm.security.Group;
import org.libreccm.security.GroupManager;
import org.libreccm.security.GroupRepository;
import org.libreccm.security.User;
import org.libreccm.security.UserManager;
import org.libreccm.security.UserRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TooManyListenersException;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
import javax.mail.MessagingException;
import static com.arsdigita.ui.admin.AdminUiConstants.*;
@ -86,6 +100,7 @@ public class UserAdmin extends BoxPanel {
// private final UserDetails userDetails;
private final BoxPanel userDetails;
private final Form emailForm;
private final Form editGroupMembershipsForm;
private final Form newUserForm;
public UserAdmin() {
@ -132,17 +147,6 @@ public class UserAdmin extends BoxPanel {
add(usersTablePanel);
// final Text text = new Text();
// text.setPrintListener((final PrintEvent e) -> {
// final Text target = (Text) e.getTarget();
// final PageState state = e.getPageState();
// if (selectedUserId.isSelected(state)) {
// target.setText(selectedUserId.getSelectedKey(state));
// }
// });
// add(text);
// userDetails = new UserDetails(this, selectedUserId);
// add(new UserDetails(this, selectedUserId));
userDetails = new BoxPanel();
userDetails.setIdAttr("userDetails");
@ -533,6 +537,71 @@ public class UserAdmin extends BoxPanel {
userDetails.add(emailTable);
final Table groupsRolesTable = new Table();
groupsRolesTable.setModelBuilder(new UserGroupsRolesTableModelBuilder(
selectedUserId));
final TableColumnModel groupsRolesColModel = groupsRolesTable
.getColumnModel();
groupsRolesColModel.add(new TableColumn(
UserGroupsRolesTableModel.COL_LABEL));
groupsRolesColModel
.add(new TableColumn(UserGroupsRolesTableModel.COL_VALUE));
groupsRolesColModel.add(
new TableColumn(UserGroupsRolesTableModel.COL_ACTION));
groupsRolesColModel.get(UserGroupsRolesTableModel.COL_ACTION)
.setCellRenderer(new TableCellRenderer() {
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
switch (row) {
case UserGroupsRolesTableModel.ROW_GROUPS: {
return new ControlLink((Component) value);
}
case UserGroupsRolesTableModel.ROW_ROLES: {
return new ControlLink((Component) value);
}
case UserGroupsRolesTableModel.ROW_ALL_ROLES:
return new Text("");
default:
throw new IllegalArgumentException();
}
}
});
groupsRolesTable.addTableActionListener(new TableActionListener() {
@Override
public void cellSelected(final TableActionEvent event) {
final int selectedRow = Integer.parseInt((String) event
.getRowKey());
final PageState state = event.getPageState();
switch (selectedRow) {
case UserGroupsRolesTableModel.ROW_GROUPS:
showEditGroupMembershipsForm(state);
break;
case UserGroupsRolesTableModel.ROW_ROLES:
//ToDo
break;
}
}
@Override
public void headSelected(final TableActionEvent event) {
//Nothing
}
}
);
userDetails.add(groupsRolesTable);
final ActionLink addEmailLink = new ActionLink(new GlobalizedMessage(
"ui.admin.user.email_addresses.add", ADMIN_BUNDLE));
addEmailLink.addActionListener(e -> {
@ -541,9 +610,6 @@ public class UserAdmin extends BoxPanel {
userDetails.add(addEmailLink);
emailForm = new Form("email_form");
// emailForm.add(new Label(new GlobalizedMessage(
// "ui.admin.user.email_form.address",
// ADMIN_BUNDLE)));
final TextField emailFormAddress = new TextField("email_form_address");
emailFormAddress.setLabel(new GlobalizedMessage(
"ui.admin.user.email_form.address", ADMIN_BUNDLE));
@ -668,7 +734,11 @@ public class UserAdmin extends BoxPanel {
add(userDetails);
editGroupMembershipsForm = buildEditGroupMembershipsForm();
add(editGroupMembershipsForm);
newUserForm = buildNewUserForm();
add(newUserForm);
}
private void setBasicProperties() {
@ -722,11 +792,24 @@ public class UserAdmin extends BoxPanel {
.addValidationListener(new StringLengthValidationListener(256));
form.add(emailField);
final FormSection setPasswordSection = new FormSection(new BoxPanel(
BoxPanel.VERTICAL));
setPasswordSection.setLabel(new GlobalizedMessage(
"ui.admin.new_user_form.password_options.set_password.label",
ADMIN_BUNDLE));
final String passwordOptions = "passwordOptions";
final String optionSetPassword = "setPassword";
final String optionSendPassword = "sendPassword";
final RadioGroup passwordOptionsGroup = new RadioGroup(passwordOptions);
final Option sendPasswordOption = new Option(
optionSendPassword,
new Label(new GlobalizedMessage(
"ui.admin.new_user_form.password_options.send_password.label",
ADMIN_BUNDLE)));
passwordOptionsGroup.addOption(sendPasswordOption);
final Option setPasswordOption = new Option(
optionSetPassword,
new Label(new GlobalizedMessage(
"ui.admin.new_user_form.password_options.set_password",
ADMIN_BUNDLE)));
passwordOptionsGroup.addOption(setPasswordOption);
form.add(passwordOptionsGroup);
final String password = "password";
final String passwordConfirmation = "passwordConfirmation";
final Password passwordField = new Password(password);
@ -738,7 +821,7 @@ public class UserAdmin extends BoxPanel {
passwordField.addValidationListener(new NotEmptyValidationListener());
passwordField.addValidationListener(new StringLengthValidationListener(
256));
setPasswordSection.add(passwordField);
form.add(passwordField);
final Password passwordConfirmationField = new Password(
passwordConfirmation);
passwordConfirmationField.setLabel(new GlobalizedMessage(
@ -750,27 +833,7 @@ public class UserAdmin extends BoxPanel {
new NotEmptyValidationListener());
passwordConfirmationField.addValidationListener(
new StringLengthValidationListener(256));
setPasswordSection.add(passwordConfirmationField);
final String passwordOptions = "passwordOptions";
final String optionSetPassword = "setPassword";
final String optionSendPassword = "sendPassword";
final RadioGroup passwordOptionsGroup = new RadioGroup(passwordOptions);
// final Option setPasswordOption = new Option(
// optionSetPassword,
// new Label(new GlobalizedMessage(
// "ui.admin.new_user_form.password_options.set_password",
// ADMIN_BUNDLE)));
final Option setPasswordOption = new Option(
optionSetPassword, setPasswordSection);
passwordOptionsGroup.addOption(setPasswordOption);
final Option sendPasswordOption = new Option(
optionSendPassword,
new Label(new GlobalizedMessage(
"ui.admin.new_user_form.password_options.send_password.label",
ADMIN_BUNDLE)));
passwordOptionsGroup.addOption(sendPasswordOption);
form.add(passwordOptionsGroup);
form.add(passwordConfirmationField);
final SaveCancelSection saveCancelSection = new SaveCancelSection();
form.add(saveCancelSection);
@ -871,6 +934,156 @@ public class UserAdmin extends BoxPanel {
return form;
}
private Form buildEditGroupMembershipsForm() {
final Form form = new Form("edit-usergroupmemberships-form");
final BoxPanel links = new BoxPanel(BoxPanel.VERTICAL);
final Label header = new Label(e -> {
final PageState state = e.getPageState();
final Label target = (Label) e.getTarget();
final String userIdStr = selectedUserId.getSelectedKey(state);
final UserRepository userRepository = CdiUtil.createCdiUtil()
.findBean(UserRepository.class);
final User user = userRepository.findById(Long.parseLong(userIdStr));
target.setLabel(new GlobalizedMessage(
"ui.admin.user.edit_group_memberships", ADMIN_BUNDLE,
new String[]{user.getName()}));
});
links.add(header);
final ActionLink backLink = new ActionLink(new GlobalizedMessage(
"ui.admin.user.edit_group_memberships.back_to_user_details",
ADMIN_BUNDLE));
backLink.addActionListener(e -> {
closeEditGroupMembershipsForm(e.getPageState());
});
links.add(backLink);
form.add(links);
final String groupsSelector = "groupsselector";
final CheckboxGroup groups = new CheckboxGroup(groupsSelector);
try {
groups.addPrintListener(e -> {
// final PageState state = e.getPageState();
final CheckboxGroup target = (CheckboxGroup) e.getTarget();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
// final UserRepository userRepository = cdiUtil.findBean(
// UserRepository.class);
final GroupRepository groupRepository = cdiUtil.findBean(
GroupRepository.class);
target.clearOptions();
final SortedSet<Group> allGroups = new TreeSet<>(
(g1, g2) -> {
return g1.getName().compareTo(g2.getName());
});
allGroups.addAll(groupRepository.findAll());
// final List<Group> assignedGroups = new ArrayList<>();
// final User user = userRepository.findById(Long.parseLong(
// selectedUserId.getSelectedKey(state)));
// user.getGroupMemberships().forEach(m -> {
// assignedGroups.add(m.getGroup());
// });
allGroups.forEach(g -> {
final Option option = new Option(
Long.toString(g.getPartyId()), new Text(g.getName()));
target.addOption(option);
// if (assignedGroups.contains(g)) {
// target.setOptionSelected(option);
// }
});
});
} catch (TooManyListenersException ex) {
throw new UncheckedWrapperException(ex);
}
form.add(groups);
final SaveCancelSection saveCancelSection = new SaveCancelSection();
form.add(saveCancelSection);
form.addInitListener(e -> {
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final UserRepository userRepository = cdiUtil.findBean(
UserRepository.class);
final PageState state = e.getPageState();
final User user = userRepository.findById(Long.parseLong(
selectedUserId.getSelectedKey(state)));
final List<Group> assignedGroups = new ArrayList<>();
user.getGroupMemberships().forEach(m -> {
assignedGroups.add(m.getGroup());
});
final String[] selectedGroups = new String[assignedGroups.size()];
IntStream.range(0, assignedGroups.size()).forEach(i -> {
selectedGroups[i] = Long.toString(assignedGroups.get(i)
.getPartyId());
});
groups.setValue(state, selectedGroups);
});
form.addProcessListener(e -> {
final PageState state = e.getPageState();
if (saveCancelSection.getSaveButton().isSelected(state)) {
final FormData data = e.getFormData();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final UserRepository userRepository = cdiUtil.findBean(
UserRepository.class);
final GroupRepository groupRepository = cdiUtil.findBean(
GroupRepository.class);
final GroupManager groupManager = cdiUtil.findBean(
GroupManager.class);
final String[] selectedGroupIds = (String[]) data.get(
groupsSelector);
final User user = userRepository.findById(Long.parseLong(
selectedUserId.getSelectedKey(state)));
final List<Group> selectedGroups = new ArrayList<>();
if (selectedGroupIds != null) {
for (String selectedGroupId : selectedGroupIds) {
final Group group = groupRepository.findById(Long
.parseLong(
selectedGroupId));
selectedGroups.add(group);
}
}
final List<Group> assignedGroups = new ArrayList<>();
user.getGroupMemberships().forEach(m -> {
assignedGroups.add(m.getGroup());
});
//First check for newly added groups
selectedGroups.forEach(g -> {
if (!assignedGroups.contains(g)) {
groupManager.addMemberToGroup(user, g);
}
});
//Than check for removed groups
assignedGroups.forEach(g -> {
if (!selectedGroups.contains(g)) {
final Group group = groupRepository.findById(
g.getPartyId());
groupManager.removeMemberFromGroup(user, group);
}
});
}
closeEditGroupMembershipsForm(state);
});
return form;
}
@Override
public void register(final Page page) {
super.register(page);
@ -883,6 +1096,7 @@ public class UserAdmin extends BoxPanel {
page.setVisibleDefault(userEditForm, false);
page.setVisibleDefault(passwordSetForm, false);
page.setVisibleDefault(emailForm, false);
page.setVisibleDefault(editGroupMembershipsForm, false);
page.setVisibleDefault(newUserForm, false);
}
@ -892,6 +1106,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -902,6 +1117,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -911,6 +1127,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, true);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -920,6 +1137,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -929,6 +1147,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, true);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -938,6 +1157,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -947,6 +1167,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, true);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -957,6 +1178,28 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
protected void showEditGroupMembershipsForm(final PageState state) {
usersTablePanel.setVisible(state, false);
userDetails.setVisible(state, false);
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, true);
newUserForm.setVisible(state, false);
}
protected void closeEditGroupMembershipsForm(final PageState state) {
selectedEmailAddress.clearSelection(state);
usersTablePanel.setVisible(state, false);
userDetails.setVisible(state, true);
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}
@ -967,6 +1210,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, true);
}
@ -977,6 +1221,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false);
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.globalization.GlobalizedMessage;
import org.libreccm.security.User;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.arsdigita.ui.admin.AdminUiConstants.*;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class UserGroupsRolesTableModel implements TableModel {
protected static final int COL_LABEL = 0;
protected static final int COL_VALUE = 1;
protected static final int COL_ACTION = 2;
protected static final int ROW_GROUPS = 0;
protected static final int ROW_ROLES = 1;
protected static final int ROW_ALL_ROLES = 2;
private int row = -1;
private final User user;
public UserGroupsRolesTableModel(final User user) {
this.user = user;
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public boolean nextRow() {
row++;
return row < 3;
}
@Override
public Object getElementAt(final int columnIndex) {
switch (row) {
case ROW_GROUPS:
return buildGroupRow(columnIndex);
case ROW_ROLES:
return buildRolesRow(columnIndex);
case ROW_ALL_ROLES:
return buildAllRolesRow(columnIndex);
default:
throw new IllegalArgumentException();
}
}
@Override
public Object getKeyAt(final int columnIndex) {
return row;
}
private Object buildGroupRow(final int columnIndex) {
switch (columnIndex) {
case COL_LABEL:
return new Label(new GlobalizedMessage("ui.admin.user.groups",
ADMIN_BUNDLE));
case COL_VALUE:
final List<String> groupNames = new ArrayList<>();
user.getGroupMemberships().forEach(m -> {
groupNames.add(m.getGroup().getName());
});
groupNames.sort((name1, name2) -> {
return name1.compareTo(name2);
});
return String.join(
", ", groupNames.toArray(new String[groupNames.size()]));
case COL_ACTION:
return new Label(new GlobalizedMessage(
"ui.admin.user.groups.edit", ADMIN_BUNDLE));
default:
throw new IllegalArgumentException();
}
}
private Object buildRolesRow(final int columnIndex) {
switch (columnIndex) {
case COL_LABEL:
return new Label(new GlobalizedMessage("ui.admin.user.roles",
ADMIN_BUNDLE));
case COL_VALUE:
final List<String> roleNames = new ArrayList<>();
user.getRoleMemberships().forEach(m -> {
roleNames.add(m.getRole().getName());
});
roleNames.sort((name1, name2) -> {
return name1.compareTo(name2);
});
return String.join(
", ", roleNames.toArray(new String[roleNames.size()]));
case COL_ACTION:
return new Label(new GlobalizedMessage(
"ui.admin.user.roles.edit", ADMIN_BUNDLE));
default:
throw new IllegalArgumentException();
}
}
private Object buildAllRolesRow(final int columnIndex) {
switch (columnIndex) {
case COL_LABEL:
return new Label(new GlobalizedMessage(
"ui.admin.user.all_roles", ADMIN_BUNDLE));
case COL_VALUE:
final Set<String> roleNames = new HashSet<>();
user.getRoleMemberships().forEach(m -> {
roleNames.add(m.getRole().getName());
});
user.getGroupMemberships().forEach(m -> {
m.getGroup().getRoleMemberships().forEach(r -> {
roleNames.add(r.getRole().getName());
});
});
final List<String> allRoleNames = new ArrayList<>(roleNames);
allRoleNames.sort((name1, name2) -> {
return name1.compareTo(name2);
});
return String.join(", ", allRoleNames.toArray(
new String[allRoleNames.size()]));
case COL_ACTION:
return "";
default:
throw new IllegalArgumentException();
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
import com.arsdigita.util.LockableImpl;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.security.User;
import org.libreccm.security.UserRepository;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class UserGroupsRolesTableModelBuilder extends LockableImpl
implements TableModelBuilder{
private final ParameterSingleSelectionModel<String> selectedUserId;
public UserGroupsRolesTableModelBuilder(
final ParameterSingleSelectionModel<String> selectedUserId) {
this.selectedUserId = selectedUserId;
}
@Override
public TableModel makeModel(final Table table, final PageState state) {
final String userIdStr = selectedUserId.getSelectedKey(state);
final User selectedUser ;
if (userIdStr == null || userIdStr.isEmpty()) {
selectedUser = null;
} else {
final UserRepository userRepository = CdiUtil.createCdiUtil()
.findBean(UserRepository.class);
final long userId = Long.parseLong(userIdStr);
selectedUser = userRepository.findById(userId);
}
return new UserGroupsRolesTableModel(selectedUser);
}
}

View File

@ -20,8 +20,10 @@ package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.PropertySheetModel;
import com.arsdigita.globalization.GlobalizedMessage;
import java.util.Arrays;
import java.util.Iterator;
import org.libreccm.security.User;
import static com.arsdigita.ui.admin.AdminUiConstants.*;
@ -38,7 +40,7 @@ public class UserPropertySheetModel implements PropertySheetModel {
GIVEN_NAME,
PASSWORD_SET,
BANNED,
PASSWORD_RESET_REQUIRED,
PASSWORD_RESET_REQUIRED
}
private final User selectedUser;
@ -102,5 +104,4 @@ public class UserPropertySheetModel implements PropertySheetModel {
return "";
}
}
}

View File

@ -159,7 +159,7 @@ public class UsersTable extends Table {
final UserRepository userRepository = CdiUtil.createCdiUtil()
.findBean(UserRepository.class);
if (filterTerm == null || filterTerm.isEmpty()) {
users = userRepository.findAll();
users = userRepository.findAllOrderdByUsername();
LOGGER.debug("Found {} users in database.", users.size());
} else {
users = userRepository.filtered(filterTerm);

View File

@ -110,6 +110,8 @@ public interface LoginConstants {
// updating existing databases (table applications)!
public static final String LOGIN_PAGE_URL = "/register/";
public static final String LOGIN_PATH = "/register";
public static final String LOGIN_SERVLET_PATH = "/login/*";
}

View File

@ -108,16 +108,25 @@ public class LoginServlet extends BebopApplicationServlet {
/**
* PathInfo into the Login application to access the <em>password reset</em>
* page which allows the user to replace a forgotten password with a new one
* (using a previously requested one time auth token). Ends with "/" because
* it is a servlet/directory
* (using a previously requested one time authentication token). Ends with
* "/" because it is a servlet/directory
*/
public static final String RESET_USER_PASSWORD_PATH_INFO = "/reset-password";
public static final String RESET_USER_PASSWORD_PATH_INFO = "/reset-password/";
/**
* PathInfo into the Login application to access the <em>verify email</em>
* page. Ends with "/" because it is a servlet/directory
* page (not implemted yet!). Ends with "/" because it is a
* servlet/directory
*/
public static final String VERIFY_EMAIL = "/verify-email/";
public static final String VERIFY_EMAIL_PATH_INFO = "/verify-email/";
/**
* PathInfo into the Login application to access the <em>confirm email</em>
* page which allows the user to confirm his/her email address by submitting
* a previously requested one time authentication token (not implemented!).
* Ends with "/" because it is a servlet/directory
*/
public static final String CONFIRM_EMAIL_PATH_INFO = "/verify-email/";
/**
* PathInfo into the Login application to access the (optional) <em>explain

View File

@ -32,6 +32,8 @@ import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.security.ChallengeManager;
import org.libreccm.security.User;
@ -48,6 +50,9 @@ import static com.arsdigita.ui.login.LoginServlet.*;
*/
public class RecoverPasswordForm extends Form {
private static final Logger LOGGER = LogManager.getLogger(
RecoverPasswordForm.class);
private static final String EMAIL = "email";
private BoxPanel formPanel;
@ -93,6 +98,7 @@ public class RecoverPasswordForm extends Form {
LOGIN_BUNDLE)),
LOGIN_PAGE_URL + RESET_USER_PASSWORD_PATH_INFO);
finishedMessagePanel.add(link);
add(finishedMessagePanel);
}
private void addListeners() {
@ -155,9 +161,14 @@ public class RecoverPasswordForm extends Form {
}
}
if (user == null) {
LOGGER.warn(
"Password recover requested for not existing user {}.",
data.get(EMAIL));
}
formPanel.setVisible(state, false);
finishedMessagePanel.setVisible(state, true);
data.clear();
}
}
);

View File

@ -23,6 +23,7 @@ import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection;
@ -31,6 +32,7 @@ import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.web.URL;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager;
@ -45,6 +47,8 @@ import org.libreccm.security.UserRepository;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import static com.arsdigita.ui.login.LoginConstants.*;
/**
@ -136,12 +140,32 @@ public class ResetPasswordForm extends Form {
successPanel = new BoxPanel(BoxPanel.VERTICAL);
successPanel.add(new Label(new GlobalizedMessage(
"login.form.reset_password.scucess", LOGIN_BUNDLE)));
successPanel.add(new Link(new Label(new GlobalizedMessage(
"login.form.reset_password.scucess.login",
LOGIN_BUNDLE)),
URL.there(LOGIN_PAGE_URL, null).getURL()));
add(successPanel);
}
private void addListeners() {
addInitListener(e -> {
final PageState state = e.getPageState();
final HttpServletRequest request = state.getRequest();
final String paramEmail = request.getParameter("email");
final String paramToken = request.getParameter("token");
if (paramEmail != null) {
email.setValue(state, paramEmail);
}
if (paramToken != null) {
authToken.setValue(state, paramToken);
}
});
addValidationListener(e -> {
final PageState state = e.getPageState();
@ -177,7 +201,7 @@ public class ResetPasswordForm extends Form {
final List<OneTimeAuthToken> tokens = oneTimeAuthManager
.retrieveForUser(
user, OneTimeAuthTokenPurpose.ACCOUNT_ACTIVATION);
user, OneTimeAuthTokenPurpose.RECOVER_PASSWORD);
boolean result = false;
for (OneTimeAuthToken token : tokens) {
@ -234,13 +258,12 @@ public class ResetPasswordForm extends Form {
throw new FormProcessException(
"Failed to finish password recovery.",
new GlobalizedMessage(
"login.form.account_activation.error.failed"),
"login.form.password_reset.error.failed"),
ex);
}
formPanel.setVisible(state, false);
successPanel.setVisible(state, true);
data.clear();
}
});
}

View File

@ -23,6 +23,7 @@ import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection;
@ -30,6 +31,7 @@ import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.web.URL;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager;
@ -44,6 +46,8 @@ import org.libreccm.security.UserRepository;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import static com.arsdigita.ui.login.LoginConstants.*;
/**
@ -102,11 +106,30 @@ public class UserAccountActivationForm extends Form {
successPanel = new BoxPanel(BoxPanel.VERTICAL);
successPanel.add(new Label(new GlobalizedMessage(
"login.form.account_activation.success", LOGIN_BUNDLE)));
successPanel.add(new Link(new Label(
new GlobalizedMessage("login.form.account_activation.success.login",
LOGIN_BUNDLE)),
URL.there(LOGIN_PAGE_URL, null).getURL()));
add(successPanel);
}
private void addListeners() {
addInitListener(e -> {
final PageState state = e.getPageState();
final HttpServletRequest request = state.getRequest();
final String paramEmail = request.getParameter("email");
final String paramToken = request.getParameter("token");
if (paramEmail != null) {
email.setValue(state, paramEmail);
}
if (paramToken != null) {
authToken.setValue(state, paramToken);
}
});
addValidationListener(e -> {
final PageState state = e.getPageState();
@ -193,7 +216,6 @@ public class UserAccountActivationForm extends Form {
formPanel.setVisible(state, false);
successPanel.setVisible(state, true);
data.clear();
}
});
}

View File

@ -32,6 +32,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
@ -208,8 +209,7 @@ public abstract class AbstractEntityRepository<K, E> {
public List<E> findAll(final String entityGraphName) {
@SuppressWarnings("unchecked")
final EntityGraph<E> entityGraph = (EntityGraph<E>) entityManager
.getEntityGraph(
entityGraphName);
.getEntityGraph(entityGraphName);
return findAll(entityGraph);
}
@ -286,9 +286,9 @@ public abstract class AbstractEntityRepository<K, E> {
}
/**
* Overwrite this method to initialise new entities with default values.
* One example is assigning a (random) UUID to new entity which implements
* the {@link Identifiable} interface.
* Overwrite this method to initialise new entities with default values. One
* example is assigning a (random) UUID to new entity which implements the
* {@link Identifiable} interface.
*
* @param entity The entity to init.
*/

View File

@ -54,10 +54,11 @@ import javax.servlet.http.HttpSession;
* </li>
* </ol>
*
* A historic note: This CDI bean replaces the old {@code GlobalizationHelper} class which used
* static methods and relied on the old {@code DispatcherHelper} for getting the
* current request. In a CDI environment we can simply inject the current request
* and don't need to bother with static methods etc.
* A historic note: This CDI bean replaces the old {@code GlobalizationHelper}
* class which used static methods and relied on the old
* {@code DispatcherHelper} for getting the current request. In a CDI
* environment we can simply inject the current request and don't need to bother
* with static methods etc.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ -73,13 +74,12 @@ public class GlobalizationHelper {
private ConfigurationManager confManager;
// private final KernelConfig kernelConfig;
// public GlobalizationHelper() {
// kernelConfig = confManager.findConfiguration(KernelConfig.class);
// }
public Locale getNegotiatedLocale() {
final KernelConfig kernelConfig = confManager.findConfiguration(KernelConfig.class);
final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class);
Locale preferred = new Locale(kernelConfig.getDefaultLanguage());
@ -91,7 +91,7 @@ public class GlobalizationHelper {
while (acceptedLocales.hasMoreElements()) {
final Locale current = acceptedLocales.nextElement();
if (kernelConfig.hasLanguage(current.getLanguage())) {
preferred = current;
preferred = new Locale(current.getLanguage());
break;
}
}

View File

@ -20,8 +20,13 @@ package org.libreccm.security;
import com.arsdigita.kernel.KernelConfig;
import com.arsdigita.mail.Mail;
import com.arsdigita.ui.login.LoginConstants;
import com.arsdigita.web.ParameterMap;
import com.arsdigita.web.URL;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.configuration.LocalizedStringSetting;
import org.libreccm.l10n.GlobalizationHelper;
@ -37,6 +42,9 @@ import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import static com.arsdigita.ui.login.LoginServlet.*;
/**
* A service class for managing several so called challenges. These challenges
@ -73,6 +81,9 @@ import javax.servlet.ServletContext;
@RequestScoped
public class ChallengeManager {
private static final Logger LOGGER = LogManager.getLogger(
ChallengeManager.class);
@Inject
private GlobalizationHelper globalizationHelper;
@ -89,7 +100,7 @@ public class ChallengeManager {
private UserManager userManager;
@Inject
private ServletContext servletContext;
private HttpServletRequest request;
public String createEmailVerification(final User user) {
if (user == null) {
@ -137,7 +148,7 @@ public class ChallengeManager {
public void sendAccountActivation(final User user)
throws MessagingException {
final String text = createEmailVerification(user);
final String text = createAccountActivation(user);
sendMessage(
user,
retrieveEmailSubject(OneTimeAuthTokenPurpose.ACCOUNT_ACTIVATION),
@ -172,7 +183,7 @@ public class ChallengeManager {
public void sendPasswordRecover(final User user)
throws MessagingException {
final String text = createEmailVerification(user);
final String text = createPasswordRecover(user);
sendMessage(
user,
retrieveEmailSubject(OneTimeAuthTokenPurpose.RECOVER_PASSWORD),
@ -211,13 +222,13 @@ public class ChallengeManager {
final String path;
switch (purpose) {
case ACCOUNT_ACTIVATION:
path = "activate-account";
path = ACTIVATE_ACCOUNT_PATH_INFO;
break;
case EMAIL_VERIFICATION:
path = "verify-email";
path = VERIFY_EMAIL_PATH_INFO;
break;
case RECOVER_PASSWORD:
path = "recover-password";
path = RESET_USER_PASSWORD_PATH_INFO;
break;
default:
throw new IllegalArgumentException(String.format(
@ -225,17 +236,28 @@ public class ChallengeManager {
purpose.toString()));
}
values.put("link",
String.format("%s/%s/register/%s",
servletContext.getVirtualServerName(),
servletContext.getContextPath(),
path));
URL.there(request,
LoginConstants.LOGIN_PATH + path, null)
.getURL());
final ParameterMap params = new ParameterMap();
params.setParameter("email", user.getPrimaryEmailAddress().getAddress());
params.setParameter("token", token.getToken());
values.put("full_link",
URL.there(request,
LoginConstants.LOGIN_PATH + path, params)
.getURL());
values.put("token", token.getToken());
final StrSubstitutor substitutor = new StrSubstitutor(values);
return substitutor.replace(template);
}
private String retrieveEmailSubject(final OneTimeAuthTokenPurpose purpose) {
LOGGER.debug("Retreving email subject...");
final Locale locale = globalizationHelper.getNegotiatedLocale();
LOGGER.debug("Negoiated locale is {}.", locale.toString());
final EmailTemplates emailTemplates = configurationManager
.findConfiguration(EmailTemplates.class);

View File

@ -67,8 +67,17 @@ public final class EmailTemplates {
"Please follow the following link to finish the email verfication "
+ "process:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "or go to\n"
+ "\n"
+ "${link}\n"
+ "\n"
+ "and enter your email address and the following verification "
+ "token:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Please be aware that your verification token expires "
+ "at ${expires_date}.");
emailVerificationMail.getValue().addValue(
@ -76,8 +85,17 @@ public final class EmailTemplates {
"Bitte folgen Sie dem folgenden Link, um die Überprüfung ihrer E-"
+ "Mail-Adresse abzuschließen:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "oder rufen Sie\n"
+ "\n"
+ "${link}\n"
+ "\n"
+ "auf und geben Sie Ihre E-Mail-Adresse und das folgende Token "
+ "ein:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Bitte beachten Sie, dass Sie den Prozess bis zu folgendem "
+ "Zeitpunkt abschließen müssen: ${expires_date}");
@ -95,17 +113,33 @@ public final class EmailTemplates {
"Please follow the following link to complete the password recover "
+ "process:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "or go to\n"
+ "${link}\n"
+ "\n"
+ "and enter your email address and the following token:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Please be aware that you must complete the process until "
+ "${expires_date}");
+ "${expires_date}.");
passwordRecoverMail.getValue().addValue(
Locale.GERMAN,
"Bitte folgen Sie dem folgenden Link um ein neues Passwort "
+ "einzugeben:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "oder rufen Sie\n"
+ "\n"
+ "${link}\n"
+ "\n"
+ "auf und geben Sie Ihre E-Mail-adresse und der folgende Token"
+ "ein:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Bitte beachten Sie, dass den den Prozess bis zu folgenden "
+ "Zeitpunkt abschließen müsssen: ${expires_date}");
@ -122,8 +156,16 @@ public final class EmailTemplates {
Locale.ENGLISH,
"Please follow the following link to enable your new account:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "or got to\n"
+ "\n"
+ "${link}\n"
+ "\n"
+ "and enter your email address and the following token:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Please be aware that you must activate your account before "
+ "${expires_date}.");
accountActivationMail.getValue().addValue(
@ -131,8 +173,17 @@ public final class EmailTemplates {
"Bitte folgen Sie den folgendem Link, um ihr Benutzerkonto zu "
+ "aktivieren:\n"
+ "\n"
+ "${link}"
+ "\n\n"
+ "${full_link}\n"
+ "\n"
+ "oder rufen Sie\n"
+ "\n"
+ "${link}\n"
+ "\n"
+ "auf und geben Sie Ihre E-Mail-Adresse und das folgende Token "
+ "ein:\n"
+ "\n"
+ "${token}\n"
+ "\n"
+ "Bitte beachten Sie, dass Sie ihr Benutzerkonto spätestens "
+ "bis zu folgendem Zeitpunkt aktivieren müssen: ${expires_date}");

View File

@ -20,6 +20,8 @@ package org.libreccm.security;
import static org.libreccm.core.CoreConstants.*;
import org.libreccm.core.DefaultEntityGraph;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
@ -27,8 +29,12 @@ import java.util.Objects;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlElement;
@ -53,6 +59,29 @@ import javax.xml.bind.annotation.XmlRootElement;
query = "SELECT g FROM Group g "
+ "WHERE LOWER(g.name) LIKE '%:name%'")
})
@NamedEntityGraphs({
@NamedEntityGraph(
name = "Group.withMembersAndRoleMemberships",
attributeNodes = {
@NamedAttributeNode(
value = "memberships"),
@NamedAttributeNode(
value = "roleMemberships",
subgraph = "role")},
subgraphs = {
@NamedSubgraph(
name = "role",
attributeNodes = {
@NamedAttributeNode(value = "role",
subgraph = "permissions")
}),
@NamedSubgraph(
name = "permissions",
attributeNodes = {
@NamedAttributeNode(value = "permissions")})
})
})
@DefaultEntityGraph("Group.withMembersAndRoleMemberships")
@XmlRootElement(name = "user-group", namespace = CORE_XML_NS)
public class Group extends Party implements Serializable {
@ -119,8 +148,6 @@ public class Group extends Party implements Serializable {
return super.hashCode();
}
@Override
public String toString(final String data) {
return super.toString(String.format(", members = { %s }%s",

View File

@ -53,6 +53,7 @@ public class GroupManager {
* @param user The user to add to a group.
* @param group The group to which the user is added.
*/
@Transactional(Transactional.TxType.REQUIRED)
public void addMemberToGroup(final User user, final Group group) {
if (user == null) {
throw new IllegalArgumentException(

View File

@ -205,7 +205,11 @@ public class OneTimeAuthManager {
throw new IllegalArgumentException("Can't invalidate a token null");
}
entityManager.remove(token);
//Ensure that we have a none detached instance
final OneTimeAuthToken delete = entityManager.find(
OneTimeAuthToken.class, token.getTokenId());
entityManager.remove(delete);
}
}

View File

@ -74,7 +74,10 @@ public class OneTimeAuthTokenCleaner {
// final long interval = 60 * 60 * 1000;
LOGGER.debug("Creating interval for {} s.", interval / 1000);
timerService.createIntervalTimer(interval, interval, new TimerConfig());
LOGGER.debug("First run cleaning process will be executed in 5 min.");
timerService.createIntervalTimer(5 * 60 * 1000,
interval,
new TimerConfig());
}
@Timeout
@ -88,7 +91,7 @@ public class OneTimeAuthTokenCleaner {
LOGGER.debug("Found {} one time auth tokens.", tokens.size());
if (LOGGER.isDebugEnabled()) {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
LOGGER.debug("Current time is: {}", now);
LOGGER.debug("Current time (UTC) is: {}", now);
tokens.forEach(t -> {
if (oneTimeAuthManager.isValid(t)) {
LOGGER.debug("OneTimeAuthToken with id {} is still valid. "
@ -97,7 +100,7 @@ public class OneTimeAuthTokenCleaner {
t.getValidUntil());
} else {
LOGGER.debug("OneTimeAuthToken with id {} is invalid. "
+ "Expires at {}.",
+ "Expires at {} UTC.",
t.getTokenId(),
t.getValidUntil());
}
@ -107,7 +110,7 @@ public class OneTimeAuthTokenCleaner {
tokens.stream()
.filter((token) -> (!oneTimeAuthManager.isValid(token)))
.forEach((token) -> {
LOGGER.debug("Token with id {} expired at {}. "
LOGGER.debug("Token with id {} expired at {} UTC. "
+ "Invalidating token.",
token.getTokenId(), token.getValidUntil());
oneTimeAuthManager.invalidate(token);

View File

@ -74,7 +74,13 @@ import javax.xml.bind.annotation.XmlTransient;
+ "LOWER(u.name) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.givenName) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.familyName) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.primaryEmailAddress.address) LIKE CONCAT('%', LOWER(:term), '%')")
+ "OR LOWER(u.primaryEmailAddress.address) LIKE CONCAT('%', LOWER(:term), '%')"),
@NamedQuery(
name = "User.findAllOrderedByUsername",
query = "SELECT u FROM User u ORDER BY u.name, "
+ " u.familyName, "
+ " u.givenName, "
+ " u.primaryEmailAddress.address")
})
@NamedEntityGraphs({
@NamedEntityGraph(
@ -98,7 +104,6 @@ import javax.xml.bind.annotation.XmlTransient;
@NamedAttributeNode(value = "permissions")}
)
})
})
@DefaultEntityGraph("User.withGroupAndRoleMemberships")
@XmlRootElement(name = "user", namespace = CORE_XML_NS)

View File

@ -58,7 +58,8 @@ public class UserManager {
* @param name The name of the new user.
* @param emailAddress The email address of the new user.
* @param password The password of the new user. The password is hashed
* using the algorithm configured in the {@link SecurityConfig}.
* using the algorithm configured in the
* {@link SecurityConfig}.
*
* @return The new user.
*/
@ -77,7 +78,11 @@ public class UserManager {
email.setAddress(emailAddress);
user.setPrimaryEmailAddress(email);
email.setVerified(true);
if (password == null) {
user.setPassword(null);
} else {
user.setPassword(hashPassword(password));
}
userRepository.save(user);
@ -142,7 +147,8 @@ public class UserManager {
//We want to use the Shiro1 format for storing the password. This
//format includes the algorithm used, the salt and the number of
//iterations used and the hashed password in special formatted string.
final HashFormatFactory hashFormatFactory = new DefaultHashFormatFactory();
final HashFormatFactory hashFormatFactory
= new DefaultHashFormatFactory();
final HashFormat hashFormat = hashFormatFactory.getInstance(
Shiro1CryptFormat.class.getName());
@ -163,8 +169,10 @@ public class UserManager {
"Salt length is not a multipe of 8");
}
final SecureRandomNumberGenerator generator = new SecureRandomNumberGenerator();
final SecureRandomNumberGenerator generator
= new SecureRandomNumberGenerator();
final int byteSize = generatedSaltSize / 8; //generatedSaltSize is in *bits* - convert to byte size:
return generator.nextBytes(byteSize);
}
}

View File

@ -153,4 +153,10 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
return query.getResultList();
}
public List<User> findAllOrderdByUsername() {
final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findAllOrderedByUsername", User.class);
return query.getResultList();
}
}

View File

@ -228,3 +228,12 @@ ui.admin.new_user_form.error.username_already_in_use=The provided user name is a
ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
ui.admin.user_details.groups=Groups
ui.admin.user.groups.edit=Edit
ui.admin.user.roles=Directly assigned Roles
ui.admin.user.roles.edit=Edit
ui.admin.user.all_roles=All roles
ui.admin.user.groups=Groups
ui.admin.user.edit_group_memberships=Edit group memberships for user {0}
ui.admin.user.edit_group_memberships.back_to_user_details=Back to user details

View File

@ -228,3 +228,12 @@ ui.admin.new_user_form.error.username_already_in_use=Der angegebene Benutzername
ui.admin.new_user_form.error.email_already_in_use=Die angegebene E-Mail-Adresse ist bereits mit einem Benutzerkonto verbunden.
ui.admin.new_user_form.error.password_do_not_match=Die eingegebenen Passw\u00f6rter stimmen nicht \u00fcberein.
ui.admin.new_user_form.error.failed_to_send_password=Fehler beim Senden des Passworts an den neuen Benutzer.
ui.admin.new_user_form.password_options.set_password=Passwert setzen
ui.admin.user_details.groups=Gruppen
ui.admin.user.groups.edit=Bearbeiten
ui.admin.user.roles=Zugewiesene Rollen
ui.admin.user.roles.edit=Bearbeiten
ui.admin.user.all_roles=Alle Rollen
ui.admin.user.groups=Gruppen
ui.admin.user.edit_group_memberships=Gruppen f\u00fcr Benutzer {0} bearbeiten
ui.admin.user.edit_group_memberships.back_to_user_details=Zur\u00fcck zu den Eigenschaften des Benutzers

View File

@ -201,3 +201,12 @@ ui.admin.new_user_form.error.username_already_in_use=The provided user name is a
ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
ui.admin.user_details.groups=Groups
ui.admin.user.groups.edit=Edit
ui.admin.user.roles=Directly assigned Roles
ui.admin.user.roles.edit=Edit
ui.admin.user.all_roles=All roles
ui.admin.user.groups=Groups
ui.admin.user.edit_group_memberships=Edit group memberships for user {0}
ui.admin.user.edit_group_memberships.back_to_user_details=Back to user details

View File

@ -192,3 +192,12 @@ ui.admin.new_user_form.error.username_already_in_use=The provided user name is a
ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
ui.admin.user_details.groups=Groups
ui.admin.user.groups.edit=Edit
ui.admin.user.roles=Directly assigned Roles
ui.admin.user.roles.edit=Edit
ui.admin.user.all_roles=All roles
ui.admin.user.groups=Groups
ui.admin.user.edit_group_memberships=Edit group memberships for user {0}
ui.admin.user.edit_group_memberships.back_to_user_details=Back to user details

View File

@ -101,3 +101,9 @@ login.form.reset_password.auth_token.hint=Please provide the one time authentica
login.form.account_activation.success=Your account has been activated.
login.form.new_user.error.passwords_do_not_match=Password and confirmation do not match.
login.form_new_user.error.creating_challenge_failed=Failed to send registration confirmation. Please contact the administrator.
login.form.account_activation.success.login=Click here to login.
login.form.reset_password.error=Error while resetting password.
login.form.reset_password.error.password_mismatch=Password and confirmation do not match.
login.form.new_user.error.username_already_in_use=The provided user name is already in use. Please choose another user name.
login.form.new_user.error.email_already_registered=There is an account already registered for the provided email address.
login.form.reset_password.scucess.login=Click here to login

View File

@ -101,3 +101,9 @@ login.form.reset_password.auth_token.hint=Bitte geben Sie das Einmalpasswort, da
login.form.account_activation.success=Ihr Benutzerkonto wurde aktiviert.
login.form.new_user.error.passwords_do_not_match=Passwort und Best\u00e4tigung sind nicht gleich
login.form_new_user.error.creating_challenge_failed=Fehler beim Senden der Anmeldebest\u00e4tigung. Bitte kontaktieren Sie den Systemadministrator.
login.form.account_activation.success.login=Klicken Sie hier, um sich anzumelden.
login.form.reset_password.error=Beim \u00c4ndern des Passwortes ist ein Fehler aufgetreten.
login.form.reset_password.error.password_mismatch=Passwort und Best\u00e4tigung stimmen nicht \u00fcberein.
login.form.new_user.error.username_already_in_use=Der eingegeben Benutzername ist bereits vergeben. Bitte w\u00e4hlen Sie einen anderen Benutzernamen.
login.form.new_user.error.email_already_registered=Es gibt bereits ein Benutzerkonto f\u00fcr die eingebene E-Mail-Adresse
login.form.reset_password.scucess.login=Zur Anmeldung

View File

@ -101,3 +101,9 @@ login.form.reset_password.auth_token.hint=Please provide the one time authentica
login.form.account_activation.success=Your account has been activated.
login.form.new_user.error.passwords_do_not_match=Password and confirmation do not match.
login.form_new_user.error.creating_challenge_failed=Failed to send registration confirmation. Please contact the administrator.
login.form.account_activation.success.login=Click here to login.
login.form.reset_password.error=Error while resetting password.
login.form.reset_password.error.password_mismatch=Password and confirmation do not match.
login.form.new_user.error.username_already_in_use=The provided user name is already in use. Please choose another user name.
login.form.new_user.error.email_already_registered=There is an account already registered for the provided email address.
login.form.reset_password.scucess.login=Click here to login

View File

@ -101,3 +101,9 @@ login.form.reset_password.auth_token.hint=Please provide the one time authentica
login.form.account_activation.success=Your account has been activated.
login.form.new_user.error.passwords_do_not_match=Password and confirmation do not match.
login.form_new_user.error.creating_challenge_failed=Failed to send registration confirmation. Please contact the administrator.
login.form.account_activation.success.login=Click here to login.
login.form.reset_password.error=Error while resetting password.
login.form.reset_password.error.password_mismatch=Password and confirmation do not match.
login.form.new_user.error.username_already_in_use=The provided user name is already in use. Please choose another user name.
login.form.new_user.error.email_already_registered=There is an account already registered for the provided email address.
login.form.reset_password.scucess.login=Click here to login