- 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.ActionLink;
import com.arsdigita.bebop.BoxPanel; import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink; import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Form; import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData; import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.FormSection;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page; import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; 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.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel; import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.UncheckedWrapperException;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.EmailAddress; import org.libreccm.core.EmailAddress;
import org.libreccm.security.ChallengeManager; 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.User;
import org.libreccm.security.UserManager; import org.libreccm.security.UserManager;
import org.libreccm.security.UserRepository; 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 javax.mail.MessagingException;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
@ -86,6 +100,7 @@ public class UserAdmin extends BoxPanel {
// private final UserDetails userDetails; // private final UserDetails userDetails;
private final BoxPanel userDetails; private final BoxPanel userDetails;
private final Form emailForm; private final Form emailForm;
private final Form editGroupMembershipsForm;
private final Form newUserForm; private final Form newUserForm;
public UserAdmin() { public UserAdmin() {
@ -132,17 +147,6 @@ public class UserAdmin extends BoxPanel {
add(usersTablePanel); 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 = new BoxPanel();
userDetails.setIdAttr("userDetails"); userDetails.setIdAttr("userDetails");
@ -533,6 +537,71 @@ public class UserAdmin extends BoxPanel {
userDetails.add(emailTable); 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( final ActionLink addEmailLink = new ActionLink(new GlobalizedMessage(
"ui.admin.user.email_addresses.add", ADMIN_BUNDLE)); "ui.admin.user.email_addresses.add", ADMIN_BUNDLE));
addEmailLink.addActionListener(e -> { addEmailLink.addActionListener(e -> {
@ -541,9 +610,6 @@ public class UserAdmin extends BoxPanel {
userDetails.add(addEmailLink); userDetails.add(addEmailLink);
emailForm = new Form("email_form"); 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"); final TextField emailFormAddress = new TextField("email_form_address");
emailFormAddress.setLabel(new GlobalizedMessage( emailFormAddress.setLabel(new GlobalizedMessage(
"ui.admin.user.email_form.address", ADMIN_BUNDLE)); "ui.admin.user.email_form.address", ADMIN_BUNDLE));
@ -668,13 +734,17 @@ public class UserAdmin extends BoxPanel {
add(userDetails); add(userDetails);
editGroupMembershipsForm = buildEditGroupMembershipsForm();
add(editGroupMembershipsForm);
newUserForm = buildNewUserForm(); newUserForm = buildNewUserForm();
add(newUserForm);
} }
private void setBasicProperties() { private void setBasicProperties() {
setIdAttr("userAdmin"); setIdAttr("userAdmin");
} }
private Form buildNewUserForm() { private Form buildNewUserForm() {
final Form form = new Form("new_user_form"); final Form form = new Form("new_user_form");
@ -722,11 +792,24 @@ public class UserAdmin extends BoxPanel {
.addValidationListener(new StringLengthValidationListener(256)); .addValidationListener(new StringLengthValidationListener(256));
form.add(emailField); form.add(emailField);
final FormSection setPasswordSection = new FormSection(new BoxPanel( final String passwordOptions = "passwordOptions";
BoxPanel.VERTICAL)); final String optionSetPassword = "setPassword";
setPasswordSection.setLabel(new GlobalizedMessage( final String optionSendPassword = "sendPassword";
"ui.admin.new_user_form.password_options.set_password.label", final RadioGroup passwordOptionsGroup = new RadioGroup(passwordOptions);
ADMIN_BUNDLE)); 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 password = "password";
final String passwordConfirmation = "passwordConfirmation"; final String passwordConfirmation = "passwordConfirmation";
final Password passwordField = new Password(password); final Password passwordField = new Password(password);
@ -738,7 +821,7 @@ public class UserAdmin extends BoxPanel {
passwordField.addValidationListener(new NotEmptyValidationListener()); passwordField.addValidationListener(new NotEmptyValidationListener());
passwordField.addValidationListener(new StringLengthValidationListener( passwordField.addValidationListener(new StringLengthValidationListener(
256)); 256));
setPasswordSection.add(passwordField); form.add(passwordField);
final Password passwordConfirmationField = new Password( final Password passwordConfirmationField = new Password(
passwordConfirmation); passwordConfirmation);
passwordConfirmationField.setLabel(new GlobalizedMessage( passwordConfirmationField.setLabel(new GlobalizedMessage(
@ -750,27 +833,7 @@ public class UserAdmin extends BoxPanel {
new NotEmptyValidationListener()); new NotEmptyValidationListener());
passwordConfirmationField.addValidationListener( passwordConfirmationField.addValidationListener(
new StringLengthValidationListener(256)); new StringLengthValidationListener(256));
setPasswordSection.add(passwordConfirmationField); form.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);
final SaveCancelSection saveCancelSection = new SaveCancelSection(); final SaveCancelSection saveCancelSection = new SaveCancelSection();
form.add(saveCancelSection); form.add(saveCancelSection);
@ -871,6 +934,156 @@ public class UserAdmin extends BoxPanel {
return form; 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 @Override
public void register(final Page page) { public void register(final Page page) {
super.register(page); super.register(page);
@ -883,6 +1096,7 @@ public class UserAdmin extends BoxPanel {
page.setVisibleDefault(userEditForm, false); page.setVisibleDefault(userEditForm, false);
page.setVisibleDefault(passwordSetForm, false); page.setVisibleDefault(passwordSetForm, false);
page.setVisibleDefault(emailForm, false); page.setVisibleDefault(emailForm, false);
page.setVisibleDefault(editGroupMembershipsForm, false);
page.setVisibleDefault(newUserForm, false); page.setVisibleDefault(newUserForm, false);
} }
@ -892,6 +1106,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -902,6 +1117,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -911,6 +1127,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, true); userEditForm.setVisible(state, true);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -920,6 +1137,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -929,6 +1147,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, true); passwordSetForm.setVisible(state, true);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -938,6 +1157,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -947,6 +1167,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, true); emailForm.setVisible(state, true);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, false); newUserForm.setVisible(state, false);
} }
@ -957,6 +1178,28 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.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); newUserForm.setVisible(state, false);
} }
@ -967,6 +1210,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.setVisible(state, true); newUserForm.setVisible(state, true);
} }
@ -977,6 +1221,7 @@ public class UserAdmin extends BoxPanel {
userEditForm.setVisible(state, false); userEditForm.setVisible(state, false);
passwordSetForm.setVisible(state, false); passwordSetForm.setVisible(state, false);
emailForm.setVisible(state, false); emailForm.setVisible(state, false);
editGroupMembershipsForm.setVisible(state, false);
newUserForm.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.bebop.PropertySheetModel;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import org.libreccm.security.User; import org.libreccm.security.User;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
@ -38,7 +40,7 @@ public class UserPropertySheetModel implements PropertySheetModel {
GIVEN_NAME, GIVEN_NAME,
PASSWORD_SET, PASSWORD_SET,
BANNED, BANNED,
PASSWORD_RESET_REQUIRED, PASSWORD_RESET_REQUIRED
} }
private final User selectedUser; private final User selectedUser;
@ -102,5 +104,4 @@ public class UserPropertySheetModel implements PropertySheetModel {
return ""; return "";
} }
} }
} }

View File

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

View File

@ -109,6 +109,8 @@ public interface LoginConstants {
// Don't modify without adapting instantiation in Loader class and // Don't modify without adapting instantiation in Loader class and
// updating existing databases (table applications)! // updating existing databases (table applications)!
public static final String LOGIN_PAGE_URL = "/register/"; public static final String LOGIN_PAGE_URL = "/register/";
public static final String LOGIN_PATH = "/register";
public static final String LOGIN_SERVLET_PATH = "/login/*"; 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> * 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 * 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 * (using a previously requested one time authentication token). Ends with
* it is a servlet/directory * "/" 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> * 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 * PathInfo into the Login application to access the (optional) <em>explain
@ -239,7 +248,7 @@ public class LoginServlet extends BebopApplicationServlet {
buildSimplePage("login.resetPasswordPage.title", buildSimplePage("login.resetPasswordPage.title",
new ResetPasswordForm(), new ResetPasswordForm(),
"reset-password")); "reset-password"));
// Build the login expire page, retrieve its URL_MSG and store in map // Build the login expire page, retrieve its URL_MSG and store in map
put(LOGIN_EXPIRED_PATH_INFO, buildExpiredPage()); put(LOGIN_EXPIRED_PATH_INFO, buildExpiredPage());

View File

@ -32,6 +32,8 @@ import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener; import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage; 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.cdi.utils.CdiUtil;
import org.libreccm.security.ChallengeManager; import org.libreccm.security.ChallengeManager;
import org.libreccm.security.User; import org.libreccm.security.User;
@ -48,6 +50,9 @@ import static com.arsdigita.ui.login.LoginServlet.*;
*/ */
public class RecoverPasswordForm extends Form { public class RecoverPasswordForm extends Form {
private static final Logger LOGGER = LogManager.getLogger(
RecoverPasswordForm.class);
private static final String EMAIL = "email"; private static final String EMAIL = "email";
private BoxPanel formPanel; private BoxPanel formPanel;
@ -93,6 +98,7 @@ public class RecoverPasswordForm extends Form {
LOGIN_BUNDLE)), LOGIN_BUNDLE)),
LOGIN_PAGE_URL + RESET_USER_PASSWORD_PATH_INFO); LOGIN_PAGE_URL + RESET_USER_PASSWORD_PATH_INFO);
finishedMessagePanel.add(link); finishedMessagePanel.add(link);
add(finishedMessagePanel);
} }
private void addListeners() { 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); formPanel.setVisible(state, false);
finishedMessagePanel.setVisible(state, true); 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.FormData;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
import com.arsdigita.bebop.Page; import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection; 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.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener; import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.web.URL;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
@ -45,6 +47,8 @@ import org.libreccm.security.UserRepository;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import static com.arsdigita.ui.login.LoginConstants.*; import static com.arsdigita.ui.login.LoginConstants.*;
/** /**
@ -115,10 +119,10 @@ public class ResetPasswordForm extends Form {
passwordConfirmation = new Password(PASSWORD_CONFIRMATION); passwordConfirmation = new Password(PASSWORD_CONFIRMATION);
passwordConfirmation.setLabel(new GlobalizedMessage( passwordConfirmation.setLabel(new GlobalizedMessage(
"login.form.reset_password.password_confirmation.label", "login.form.reset_password.password_confirmation.label",
LOGIN_BUNDLE)); LOGIN_BUNDLE));
passwordConfirmation.setHint(new GlobalizedMessage( passwordConfirmation.setHint(new GlobalizedMessage(
"login.form.reset_password.password_confirmation.hint", "login.form.reset_password.password_confirmation.hint",
LOGIN_BUNDLE)); LOGIN_BUNDLE));
passwordConfirmation.setMaxLength(256); passwordConfirmation.setMaxLength(256);
passwordConfirmation.setSize(32); passwordConfirmation.setSize(32);
@ -136,12 +140,32 @@ public class ResetPasswordForm extends Form {
successPanel = new BoxPanel(BoxPanel.VERTICAL); successPanel = new BoxPanel(BoxPanel.VERTICAL);
successPanel.add(new Label(new GlobalizedMessage( successPanel.add(new Label(new GlobalizedMessage(
"login.form.reset_password.scucess", LOGIN_BUNDLE))); "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); add(successPanel);
} }
private void addListeners() { 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 -> { addValidationListener(e -> {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
@ -177,7 +201,7 @@ public class ResetPasswordForm extends Form {
final List<OneTimeAuthToken> tokens = oneTimeAuthManager final List<OneTimeAuthToken> tokens = oneTimeAuthManager
.retrieveForUser( .retrieveForUser(
user, OneTimeAuthTokenPurpose.ACCOUNT_ACTIVATION); user, OneTimeAuthTokenPurpose.RECOVER_PASSWORD);
boolean result = false; boolean result = false;
for (OneTimeAuthToken token : tokens) { for (OneTimeAuthToken token : tokens) {
@ -234,13 +258,12 @@ public class ResetPasswordForm extends Form {
throw new FormProcessException( throw new FormProcessException(
"Failed to finish password recovery.", "Failed to finish password recovery.",
new GlobalizedMessage( new GlobalizedMessage(
"login.form.account_activation.error.failed"), "login.form.password_reset.error.failed"),
ex); ex);
} }
formPanel.setVisible(state, false); formPanel.setVisible(state, false);
successPanel.setVisible(state, true); 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.FormData;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
import com.arsdigita.bebop.Page; import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection; 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.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener; import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.web.URL;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
@ -44,6 +46,8 @@ import org.libreccm.security.UserRepository;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import static com.arsdigita.ui.login.LoginConstants.*; import static com.arsdigita.ui.login.LoginConstants.*;
/** /**
@ -102,11 +106,30 @@ public class UserAccountActivationForm extends Form {
successPanel = new BoxPanel(BoxPanel.VERTICAL); successPanel = new BoxPanel(BoxPanel.VERTICAL);
successPanel.add(new Label(new GlobalizedMessage( successPanel.add(new Label(new GlobalizedMessage(
"login.form.account_activation.success", LOGIN_BUNDLE))); "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); add(successPanel);
} }
private void addListeners() { 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 -> { addValidationListener(e -> {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
@ -193,7 +216,6 @@ public class UserAccountActivationForm extends Form {
formPanel.setVisible(state, false); formPanel.setVisible(state, false);
successPanel.setVisible(state, true); successPanel.setVisible(state, true);
data.clear();
} }
}); });
} }

View File

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

View File

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

View File

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

View File

@ -67,17 +67,35 @@ public final class EmailTemplates {
"Please follow the following link to finish the email verfication " "Please follow the following link to finish the email verfication "
+ "process:\n" + "process:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\n" + "\n"
+ "Please be aware that your verification token expires" + "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}."); + "at ${expires_date}.");
emailVerificationMail.getValue().addValue( emailVerificationMail.getValue().addValue(
Locale.GERMAN, Locale.GERMAN,
"Bitte folgen Sie dem folgenden Link, um die Überprüfung ihrer E-" "Bitte folgen Sie dem folgenden Link, um die Überprüfung ihrer E-"
+ "Mail-Adresse abzuschließen:\n" + "Mail-Adresse abzuschließen:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\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 " + "Bitte beachten Sie, dass Sie den Prozess bis zu folgendem "
+ "Zeitpunkt abschließen müssen: ${expires_date}"); + "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 " "Please follow the following link to complete the password recover "
+ "process:\n" + "process:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\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 " + "Please be aware that you must complete the process until "
+ "${expires_date}"); + "${expires_date}.");
passwordRecoverMail.getValue().addValue( passwordRecoverMail.getValue().addValue(
Locale.GERMAN, Locale.GERMAN,
"Bitte folgen Sie dem folgenden Link um ein neues Passwort " "Bitte folgen Sie dem folgenden Link um ein neues Passwort "
+ "einzugeben:\n" + "einzugeben:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\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 " + "Bitte beachten Sie, dass den den Prozess bis zu folgenden "
+ "Zeitpunkt abschließen müsssen: ${expires_date}"); + "Zeitpunkt abschließen müsssen: ${expires_date}");
@ -122,8 +156,16 @@ public final class EmailTemplates {
Locale.ENGLISH, Locale.ENGLISH,
"Please follow the following link to enable your new account:\n" "Please follow the following link to enable your new account:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\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 " + "Please be aware that you must activate your account before "
+ "${expires_date}."); + "${expires_date}.");
accountActivationMail.getValue().addValue( accountActivationMail.getValue().addValue(
@ -131,9 +173,18 @@ public final class EmailTemplates {
"Bitte folgen Sie den folgendem Link, um ihr Benutzerkonto zu " "Bitte folgen Sie den folgendem Link, um ihr Benutzerkonto zu "
+ "aktivieren:\n" + "aktivieren:\n"
+ "\n" + "\n"
+ "${link}" + "${full_link}\n"
+ "\n\n" + "\n"
+ "Bitte beachten Sie, dass Sie ihr Benutzerkonto spätestens" + "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}"); + "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 static org.libreccm.core.CoreConstants.*;
import org.libreccm.core.DefaultEntityGraph;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -27,8 +29,12 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; import javax.persistence.NamedQuery;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
@ -36,12 +42,12 @@ import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
/** /**
* A group is basically a collection of users. * A group is basically a collection of users.
* *
* Group extends the {@link Party} class. Therefore {@link Role}s can be * Group extends the {@link Party} class. Therefore {@link Role}s can be
* assigned to a group. When a {@link Role} is assigned to a group each member * assigned to a group. When a {@link Role} is assigned to a group each member
* of the group gets the role and the permissions associated with that role. * of the group gets the role and the permissions associated with that role.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@Entity @Entity
@ -53,6 +59,29 @@ import javax.xml.bind.annotation.XmlRootElement;
query = "SELECT g FROM Group g " query = "SELECT g FROM Group g "
+ "WHERE LOWER(g.name) LIKE '%:name%'") + "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) @XmlRootElement(name = "user-group", namespace = CORE_XML_NS)
public class Group extends Party implements Serializable { public class Group extends Party implements Serializable {
@ -118,8 +147,6 @@ public class Group extends Party implements Serializable {
public int hashCode() { public int hashCode() {
return super.hashCode(); return super.hashCode();
} }
@Override @Override
public String toString(final String data) { public String toString(final String data) {

View File

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

View File

@ -205,7 +205,11 @@ public class OneTimeAuthManager {
throw new IllegalArgumentException("Can't invalidate a token null"); 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; // final long interval = 60 * 60 * 1000;
LOGGER.debug("Creating interval for {} s.", interval / 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 @Timeout
@ -88,7 +91,7 @@ public class OneTimeAuthTokenCleaner {
LOGGER.debug("Found {} one time auth tokens.", tokens.size()); LOGGER.debug("Found {} one time auth tokens.", tokens.size());
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
LOGGER.debug("Current time is: {}", now); LOGGER.debug("Current time (UTC) is: {}", now);
tokens.forEach(t -> { tokens.forEach(t -> {
if (oneTimeAuthManager.isValid(t)) { if (oneTimeAuthManager.isValid(t)) {
LOGGER.debug("OneTimeAuthToken with id {} is still valid. " LOGGER.debug("OneTimeAuthToken with id {} is still valid. "
@ -97,7 +100,7 @@ public class OneTimeAuthTokenCleaner {
t.getValidUntil()); t.getValidUntil());
} else { } else {
LOGGER.debug("OneTimeAuthToken with id {} is invalid. " LOGGER.debug("OneTimeAuthToken with id {} is invalid. "
+ "Expires at {}.", + "Expires at {} UTC.",
t.getTokenId(), t.getTokenId(),
t.getValidUntil()); t.getValidUntil());
} }
@ -107,7 +110,7 @@ public class OneTimeAuthTokenCleaner {
tokens.stream() tokens.stream()
.filter((token) -> (!oneTimeAuthManager.isValid(token))) .filter((token) -> (!oneTimeAuthManager.isValid(token)))
.forEach((token) -> { .forEach((token) -> {
LOGGER.debug("Token with id {} expired at {}. " LOGGER.debug("Token with id {} expired at {} UTC. "
+ "Invalidating token.", + "Invalidating token.",
token.getTokenId(), token.getValidUntil()); token.getTokenId(), token.getValidUntil());
oneTimeAuthManager.invalidate(token); oneTimeAuthManager.invalidate(token);

View File

@ -74,7 +74,13 @@ import javax.xml.bind.annotation.XmlTransient;
+ "LOWER(u.name) LIKE CONCAT(LOWER(:term), '%') " + "LOWER(u.name) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.givenName) LIKE CONCAT(LOWER(:term), '%') " + "OR LOWER(u.givenName) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.familyName) 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({ @NamedEntityGraphs({
@NamedEntityGraph( @NamedEntityGraph(
@ -98,7 +104,6 @@ import javax.xml.bind.annotation.XmlTransient;
@NamedAttributeNode(value = "permissions")} @NamedAttributeNode(value = "permissions")}
) )
}) })
}) })
@DefaultEntityGraph("User.withGroupAndRoleMemberships") @DefaultEntityGraph("User.withGroupAndRoleMemberships")
@XmlRootElement(name = "user", namespace = CORE_XML_NS) @XmlRootElement(name = "user", namespace = CORE_XML_NS)

View File

@ -53,12 +53,13 @@ public class UserManager {
* Creates a new user and saves the user in the database. The method also * Creates a new user and saves the user in the database. The method also
* creates the password hash. * creates the password hash.
* *
* @param givenName The given name of the new user. * @param givenName The given name of the new user.
* @param familyName The family name of the new user. * @param familyName The family name of the new user.
* @param name The name of the new user. * @param name The name of the new user.
* @param emailAddress The email address 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 * @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. * @return The new user.
*/ */
@ -77,7 +78,11 @@ public class UserManager {
email.setAddress(emailAddress); email.setAddress(emailAddress);
user.setPrimaryEmailAddress(email); user.setPrimaryEmailAddress(email);
email.setVerified(true); email.setVerified(true);
user.setPassword(hashPassword(password)); if (password == null) {
user.setPassword(null);
} else {
user.setPassword(hashPassword(password));
}
userRepository.save(user); userRepository.save(user);
@ -90,11 +95,11 @@ public class UserManager {
* the user can't login or that the authentication for this user is done by * the user can't login or that the authentication for this user is done by
* an external system. * an external system.
* *
* @param user The user which password should be upgraded. * @param user The user which password should be upgraded.
* @param newPassword The new password. The password is hashed using the * @param newPassword The new password. The password is hashed using the
* algorithm configured in the {@link SecurityConfig}. * algorithm configured in the {@link SecurityConfig}.
*/ */
public void updatePassword(@NotNull final User user, public void updatePassword(@NotNull final User user,
final String newPassword) { final String newPassword) {
user.setPassword(hashPassword(newPassword)); user.setPassword(hashPassword(newPassword));
@ -105,11 +110,11 @@ public class UserManager {
* Verifies the password of a user. This can be useful if you want to verify * Verifies the password of a user. This can be useful if you want to verify
* the password of a user already logged in again. * the password of a user already logged in again.
* *
* @param user The user against which the password is verified. * @param user The user against which the password is verified.
* @param password The password to verify. * @param password The password to verify.
* *
* @return {@code true} if the provided passworda matches the password from * @return {@code true} if the provided passworda matches the password from
* the database, {@code false} otherwise. * the database, {@code false} otherwise.
*/ */
public boolean verifyPassword(final User user, final String password) { public boolean verifyPassword(final User user, final String password) {
//Create a new Shiro PasswordMatcher instance //Create a new Shiro PasswordMatcher instance
@ -142,9 +147,10 @@ public class UserManager {
//We want to use the Shiro1 format for storing the password. This //We want to use the Shiro1 format for storing the password. This
//format includes the algorithm used, the salt and the number of //format includes the algorithm used, the salt and the number of
//iterations used and the hashed password in special formatted string. //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( final HashFormat hashFormat = hashFormatFactory.getInstance(
Shiro1CryptFormat.class.getName()); Shiro1CryptFormat.class.getName());
return hashFormat.format(hash); return hashFormat.format(hash);
} }
@ -160,11 +166,13 @@ public class UserManager {
if (generatedSaltSize % 8 != 0) { if (generatedSaltSize % 8 != 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Salt length is not a multipe of 8"); "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: final int byteSize = generatedSaltSize / 8; //generatedSaltSize is in *bits* - convert to byte size:
return generator.nextBytes(byteSize); return generator.nextBytes(byteSize);
} }
} }

View File

@ -149,7 +149,13 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
final TypedQuery<User> query = getEntityManager().createNamedQuery( final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.filterByNameAndEmail", User.class); "User.filterByNameAndEmail", User.class);
query.setParameter("term", term); query.setParameter("term", term);
return query.getResultList();
}
public List<User> findAllOrderdByUsername() {
final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findAllOrderedByUsername", User.class);
return query.getResultList(); 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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_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.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.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_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.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.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_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.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.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_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