From c58c0c4de5aca60f19e0973376d6f08ce9bde296 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 22 May 2018 17:04:36 +0200 Subject: [PATCH] OO-3486, OO-3476: implement a custom UI to list deleted users, delete user properties role based... --- .../olat/admin/UserAdminMainController.java | 3 +- .../admin/user/DeletedRolesCellRenderer.java | 67 ++++++++ .../admin/user/DeletedUserDataSource.java | 72 ++++++++ .../admin/user/DeletedUsersController.java | 156 ++++++++++++++++++ .../admin/user/DeletedUsersTableModel.java | 100 +++++++++++ .../user/SystemRolesAndRightsController.java | 2 +- .../user/UsermanagerUserSearchController.java | 3 +- .../admin/user/_content/deleted_users.html | 2 + .../user/_i18n/LocalStrings_de.properties | 16 +- .../user/_i18n/LocalStrings_en.properties | 15 +- .../bulkChange/UserBulkChangeManager.java | 16 +- .../user/delete/DirectDeleteController.java | 2 +- .../user/delete/ReadyToDeleteController.java | 9 +- .../delete/service/UserDeletionManager.java | 12 +- .../org/olat/basesecurity/BaseSecurity.java | 30 +++- .../basesecurity/BaseSecurityManager.java | 121 ++++++++++++-- .../DeletedIdentitiesQueries.java | 41 +++++ .../olat/basesecurity/IdentityImpl.hbm.xml | 15 +- .../org/olat/basesecurity/IdentityImpl.java | 28 ++++ .../manager/DeletedIdentitiesQueriesImpl.java | 130 +++++++++++++++ .../basesecurity/model/DeletedIdentity.java | 91 ++++++++++ .../table/DefaultFlexiColumnModel.java | 5 + .../java/org/olat/ldap/LDAPLoginManager.java | 2 +- .../ldap/manager/LDAPLoginManagerImpl.java | 6 +- .../org/olat/ldap/ui/LDAPAdminController.java | 2 +- .../ui/AbstractPageListController.java | 2 +- .../olat/portfolio/manager/InvitationDAO.java | 4 +- src/main/java/org/olat/user/UserManager.java | 2 + .../java/org/olat/user/UserManagerImpl.java | 35 +++- .../org/olat/user/restapi/UserWebService.java | 10 +- .../database/mysql/alter_12_4_x_to_12_5_0.sql | 5 + .../database/mysql/setupDatabase.sql | 3 + .../oracle/alter_12_4_x_to_12_5_0.sql | 5 + .../database/oracle/setupDatabase.sql | 3 + .../postgresql/alter_12_4_x_to_12_5_0.sql | 5 + .../database/postgresql/setupDatabase.sql | 3 + .../service/UserDeletionManagerTest.java | 57 ++++++- .../GetIdentitiesByPowerSearchTest.java | 16 +- .../manager/UserCommentsDAOTest.java | 2 +- .../manager/UserRatingsDAOTest.java | 2 +- .../InstantMessageServiceTest.java | 2 +- .../java/org/olat/ldap/LDAPLoginTest.java | 4 +- .../manager/PortfolioServiceTest.java | 2 +- .../restapi/RepositoryEntryResourceTest.java | 19 +++ src/test/java/org/olat/user/UserDAOTest.java | 6 +- src/test/java/org/olat/user/UserTest.java | 2 +- 46 files changed, 1050 insertions(+), 85 deletions(-) create mode 100644 src/main/java/org/olat/admin/user/DeletedRolesCellRenderer.java create mode 100644 src/main/java/org/olat/admin/user/DeletedUserDataSource.java create mode 100644 src/main/java/org/olat/admin/user/DeletedUsersController.java create mode 100644 src/main/java/org/olat/admin/user/DeletedUsersTableModel.java create mode 100644 src/main/java/org/olat/admin/user/_content/deleted_users.html create mode 100644 src/main/java/org/olat/basesecurity/DeletedIdentitiesQueries.java create mode 100644 src/main/java/org/olat/basesecurity/manager/DeletedIdentitiesQueriesImpl.java create mode 100644 src/main/java/org/olat/basesecurity/model/DeletedIdentity.java diff --git a/src/main/java/org/olat/admin/UserAdminMainController.java b/src/main/java/org/olat/admin/UserAdminMainController.java index a3095c5b391..f22f774a6ec 100644 --- a/src/main/java/org/olat/admin/UserAdminMainController.java +++ b/src/main/java/org/olat/admin/UserAdminMainController.java @@ -29,6 +29,7 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import org.olat.admin.user.DeletedUsersController; import org.olat.admin.user.NewUsersNotificationsController; import org.olat.admin.user.UserAdminController; import org.olat.admin.user.UserCreateController; @@ -458,7 +459,7 @@ public class UserAdminMainController extends MainLayoutBasicController implement } else if (uobject.equals("deletedusers")) { activatePaneInDetailView = "list.deletedusers"; - contentCtr = new UsermanagerUserSearchController(ureq, bwControl,null, null, null, null, null, Identity.STATUS_DELETED, false); + contentCtr = new DeletedUsersController(ureq, bwControl); addToHistory(ureq, bwControl); listenTo(contentCtr); return contentCtr.getInitialComponent(); diff --git a/src/main/java/org/olat/admin/user/DeletedRolesCellRenderer.java b/src/main/java/org/olat/admin/user/DeletedRolesCellRenderer.java new file mode 100644 index 00000000000..fa8f2f1f436 --- /dev/null +++ b/src/main/java/org/olat/admin/user/DeletedRolesCellRenderer.java @@ -0,0 +1,67 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.admin.user; + +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.StringHelper; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DeletedRolesCellRenderer implements FlexiCellRenderer { + + private final Translator translator; + + public DeletedRolesCellRenderer(Translator translator) { + this.translator = translator; + } + + @Override + public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source, + URLBuilder ubu, Translator trl) { + + if(cellValue instanceof String) { + String roles = (String)cellValue; + String[] roleArray = roles.split("[,]"); + + boolean sep = false; + for(int i=0; i<roleArray.length; i++) { + String role = roleArray[i]; + if(!StringHelper.containsNonWhitespace(role) + || "users".equals(role) || "anonymous".equals(role) || "ldap".equals(role)) continue; + + if(!sep) { + sep = true; + } else { + target.append(", "); + } + target.append(translator.translate(role)); + } + } + } +} diff --git a/src/main/java/org/olat/admin/user/DeletedUserDataSource.java b/src/main/java/org/olat/admin/user/DeletedUserDataSource.java new file mode 100644 index 00000000000..9414ee113f5 --- /dev/null +++ b/src/main/java/org/olat/admin/user/DeletedUserDataSource.java @@ -0,0 +1,72 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.admin.user; + +import java.util.Collections; +import java.util.List; + +import org.olat.basesecurity.DeletedIdentitiesQueries; +import org.olat.basesecurity.model.DeletedIdentity; +import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.persistence.DefaultResultInfos; +import org.olat.core.commons.persistence.ResultInfos; +import org.olat.core.commons.persistence.SortKey; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataSourceDelegate; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DeletedUserDataSource implements FlexiTableDataSourceDelegate<DeletedIdentity> { + + @Autowired + private DeletedIdentitiesQueries deletedIdentitiesQueries; + + public DeletedUserDataSource() { + CoreSpringFactory.autowireObject(this); + } + + @Override + public int getRowCount() { + return deletedIdentitiesQueries.countDeletedIdentities(); + } + + @Override + public List<DeletedIdentity> reload(List<DeletedIdentity> rows) { + return Collections.emptyList(); + } + + @Override + public ResultInfos<DeletedIdentity> getRows(String query, List<FlexiTableFilter> filters, + List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) { + + SortKey sortKey = null; + if(orderBy != null && orderBy.length > 0 && orderBy[0] != null) { + sortKey = orderBy[0]; + } + List<DeletedIdentity> rows = deletedIdentitiesQueries + .getIdentitiesByPowerSearch(firstResult, maxResults, sortKey); + return new DefaultResultInfos<>(firstResult + rows.size(), -1, rows); + } +} diff --git a/src/main/java/org/olat/admin/user/DeletedUsersController.java b/src/main/java/org/olat/admin/user/DeletedUsersController.java new file mode 100644 index 00000000000..6d97389a8bb --- /dev/null +++ b/src/main/java/org/olat/admin/user/DeletedUsersController.java @@ -0,0 +1,156 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.admin.user; + +import org.olat.admin.user.DeletedUsersTableModel.DeletedCols; +import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.BaseSecurityModule; +import org.olat.basesecurity.model.DeletedIdentity; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.elements.table.BooleanCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; +import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; +import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.modal.DialogBoxController; +import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; +import org.olat.core.id.Identity; +import org.olat.core.id.Roles; +import org.olat.core.util.StringHelper; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DeletedUsersController extends FormBasicController { + + private FlexiTableElement tableEl; + private DeletedUsersTableModel tableModel; + + private DialogBoxController confirmClearCtrl; + + private final boolean isAdministrativeUser; + + @Autowired + private UserManager userManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private BaseSecurityModule securityModule; + + public DeletedUsersController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl, "deleted_users"); + + Roles roles = ureq.getUserSession().getRoles(); + isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles); + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + if(isAdministrativeUser) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.username)); + } + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.firstName)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.lastName)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.deletedDate)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.lastLogin)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.creationDate)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, DeletedCols.deletedRoles, + new DeletedRolesCellRenderer(getTranslator()))); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(DeletedCols.deletedBy)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("clear", DeletedCols.clear.ordinal(), "clear", + new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("clear"), "clear"), null))); + + tableModel = new DeletedUsersTableModel(new DeletedUserDataSource(), userManager, columnsModel); + tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 25, true, getTranslator(), formLayout); + tableEl.setCustomizeColumns(true); + tableEl.setEmtpyTableMessageKey("error.no.user.found"); + tableEl.setExportEnabled(false); + tableEl.setAndLoadPersistedPreferences(ureq, "deleted-user-list"); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(confirmClearCtrl == source) { + if (DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { + doClear((Identity)confirmClearCtrl.getUserObject()); + } + } + super.event(ureq, source, event); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(tableEl == source) { + if(event instanceof SelectionEvent) { + SelectionEvent se = (SelectionEvent)event; + if("clear".equals(se.getCommand())) { + DeletedIdentity deletedIdentity = tableModel.getObject(se.getIndex()); + doConfirmClear(ureq, deletedIdentity); + } + + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + private void doConfirmClear(UserRequest ureq, DeletedIdentity deletedIdentity) { + if(!StringHelper.containsNonWhitespace(deletedIdentity.getIdentityFirstName()) + && !StringHelper.containsNonWhitespace(deletedIdentity.getIdentityLastName())) return; + + Identity identity = securityManager.loadIdentityByKey(deletedIdentity.getIdentityKey()); + String fullname = userManager.getUserDisplayName(identity); + String text = translate("confirm.clear.identity", new String[] { fullname }); + confirmClearCtrl = activateOkCancelDialog(ureq, translate("clear"), text, confirmClearCtrl); + confirmClearCtrl.setUserObject(identity); + } + + private void doClear(Identity deletedIdentity) { + userManager.clearAllUserProperties(deletedIdentity); + tableModel.clear(); + tableEl.reset(false, false, true); + } +} diff --git a/src/main/java/org/olat/admin/user/DeletedUsersTableModel.java b/src/main/java/org/olat/admin/user/DeletedUsersTableModel.java new file mode 100644 index 00000000000..7b5a53bd035 --- /dev/null +++ b/src/main/java/org/olat/admin/user/DeletedUsersTableModel.java @@ -0,0 +1,100 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.admin.user; + +import org.olat.basesecurity.model.DeletedIdentity; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataSourceModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataSourceDelegate; +import org.olat.core.util.StringHelper; +import org.olat.user.UserManager; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DeletedUsersTableModel extends DefaultFlexiTableDataSourceModel<DeletedIdentity> { + + private final UserManager userManager; + + public DeletedUsersTableModel(FlexiTableDataSourceDelegate<DeletedIdentity> source, UserManager userManager, FlexiTableColumnModel columnModel) { + super(source, columnModel); + this.userManager = userManager; + } + + @Override + public Object getValueAt(int row, int col) { + DeletedIdentity identity = getObject(row); + switch(DeletedCols.values()[col]) { + case username: return identity.getIdentityName(); + case firstName: return identity.getIdentityFirstName(); + case lastName: return identity.getIdentityLastName(); + case deletedDate: return identity.getDeletedDate(); + case lastLogin: return identity.getLastLogin(); + case creationDate: return identity.getCreationDate(); + case deletedBy: return identity.getDeletedBy(); + case deletedRoles: return identity.getDeletedRoles(); + case clear: return StringHelper.containsNonWhitespace(identity.getIdentityFirstName()) + || StringHelper.containsNonWhitespace(identity.getIdentityLastName()); + } + return null; + } + + @Override + public DefaultFlexiTableDataSourceModel<DeletedIdentity> createCopyWithEmptyList() { + return new DeletedUsersTableModel(getSourceDelegate(), userManager, getTableColumnModel()); + } + + public enum DeletedCols implements FlexiSortableColumnDef { + username("table.identity.name"), + firstName("table.name.firstName"), + lastName("table.name.lastName"), + deletedDate("table.identity.deleteddate"), + lastLogin("table.identity.lastlogin"), + creationDate("table.identity.creationdate"), + deletedRoles("table.identity.deletedroles"), + deletedBy("table.identity.deletedby"), + clear("clear"); + + private final String i18nKey; + + private DeletedCols(String i18nKey) { + this.i18nKey = i18nKey; + } + + @Override + public String i18nHeaderKey() { + return i18nKey; + } + + @Override + public boolean sortable() { + return !name().equals(deletedRoles.name()); + } + + @Override + public String sortKey() { + return name(); + } + } +} diff --git a/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java b/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java index 4b48ff84d32..145d9eec485 100644 --- a/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java +++ b/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java @@ -207,7 +207,7 @@ public class SystemRolesAndRightsController extends BasicController { userBulkChangeManager.sendLoginDeniedEmail(myIdentity); } - identity = securityManager.saveIdentityStatus(myIdentity, newStatus); + identity = securityManager.saveIdentityStatus(myIdentity, newStatus, getIdentity()); logAudit("User::" + getIdentity().getKey() + " changed accout status for user::" + myIdentity.getKey() + " from::" + oldStatusText + " to::" + newStatusText, null); } } diff --git a/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java b/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java index 19e89a10a8f..324a025b38d 100644 --- a/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java +++ b/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java @@ -583,9 +583,8 @@ public class UsermanagerUserSearchController extends BasicController implements List<Long> partGroups = (List<Long>) runContext.get("partGroups"); //List<Long> mailGroups = (List<Long>) runContext.get("mailGroups"); if (attributeChangeMap.size() != 0 || roleChangeMap.size() != 0 || ownGroups.size() != 0 || partGroups.size() != 0){ - Identity addingIdentity = ureq1.getIdentity(); ubcMan.changeSelectedIdentities(selectedIdentities, attributeChangeMap, roleChangeMap, notUpdatedIdentities, - isAdministrativeUser, ownGroups, partGroups, getTranslator(), addingIdentity); + isAdministrativeUser, ownGroups, partGroups, getTranslator(), getIdentity()); hasChanges = true; } } diff --git a/src/main/java/org/olat/admin/user/_content/deleted_users.html b/src/main/java/org/olat/admin/user/_content/deleted_users.html new file mode 100644 index 00000000000..73038be16c8 --- /dev/null +++ b/src/main/java/org/olat/admin/user/_content/deleted_users.html @@ -0,0 +1,2 @@ +<h4>$r.translate("deleted.user.list")</h4> +$r.render("table") \ No newline at end of file diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties index 78d5d4c5e6d..102d2a084e3 100644 --- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties @@ -3,10 +3,13 @@ action.bulkedit=Attribute dieser Benutzer \u00E4ndern action.choose=W\u00E4hlen action.choose.finish=W\u00E4hlen und Fertigstellen action.select=W\u00E4hlen +admins=Administrator authedit.delete.confirm=Sind Sie sicher, dass Sie den Authentifizierungs-Provider {0} f\u00FCr Benutzer {1} l\u00F6schen m\u00F6chten? authedit.delete.success=Authentifizierungs-Provider {0} f\u00FCr Benutzer {1} wurde gel\u00F6scht. autocomplete.noresults=$org.olat.core.gui.control.generic.ajax.autocompletion\:autocomplete.noresults autocompletion.info=Geben Sie einen OpenOLAT-Benutzernamen, Vornamen oder Nachnamen ein. +authors=Autor +bgroupcoach=Gruppebetreuer btn.back=Zur\u00FCck bulkChange.failed=Es wurden keinen \u00C4nderungen vorgenommen bulkChange.partialsuccess={0} von {1} \u00C4nderungen erfolgreich. Folgende Benutzer-Attribute konnten nicht ge\u00E4ndert werden\: {2} @@ -15,10 +18,13 @@ bulkChange.title=\u00C4nderung von Benutzer-Attributen changeuserpwd.cancel=Die Aktion wurde abgebrochen. Das Passwort bleibt unver\u00E4ndert. changeuserpwd.failed=Ihr neues Passwort wurde nicht gespeichert. Ein unerwarteter Fehler ist aufgetreten. changeuserpwd.successful=Das neue Passwort wurde gespeichert. Es ist ab sofort g\u00FCltig. +clear=Entfernen command.mail=E-Mail an diese Benutzer +confirm.clear.identity=Wollen Sie den Vorname und Nachname von diesem gel\u00F6schten Benutzer "{0}" entfernen? content.usermgmt.text1=Hier k\u00F6nnen Sie Daten von OLAT-Benutzern \u00E4ndern und verwalten. Bitte beginnen Sie mit der Suche nach Benutzern. content.usermgmt.title=Benutzerverwaltung content.usermgmt.userfound=Benutzereinstellungen verwalten +deleted.user.list=Liste der gel\u00F6schte Benutzer deselectall=$org.olat.core.gui.components.table\:uncheckall edit.uauth=Authentifizierungen edit.uhomepage=Visitenkarte @@ -46,9 +52,10 @@ form.token.new.text=Nachricht form.token.new.title=Passwortlink senden f\u00FCr OpenOLAT Passwort form.username=Benutzername found.property=Property ausgew\u00E4hlt {0} +groupmanagers=Gruppenverwalter header.autocompletion=Suche mit Autovervollst\u00E4ndigung header.normal=Suche mit Benutzerattributen - +instoresmanager=Lernressourcenverwalter mailtemplate.login.denied.body=Sehr geehrte/r {6} {4},\n\nIhr OpenOLAT Account {0} mit der E-Mail {1} auf dem System {5} wurde deaktiviert.\n\nWenn Sie m\u00F6chten, dass der Account wieder aktiviert wird, dann melden Sie sich bitte beim Support unter {3}.\n\n\nViele Gr\u00FCsse\nDas e-Learning Team mailtemplate.login.denied.subject=Deaktivierung ihres OpenOLAT Accounts mailto.userlist=Liste der Benutzer @@ -72,6 +79,8 @@ new.user.cancel=Die Aktion wurde abgebrochen. Es wurde keine neues Benutzerkonto new.user.successful=Das neue Benutzerkonto wurde angelegt. notification.noNews=Seit diesem Datum haben sich keine neue Benutzer angemeldet notification.noSubscription=Sie haben Benachrichtigung \u00FCber neue Benutzer nicht abonniert +poolsmanager=Poolverwalter +repocoach=Kursbetreuer rightForm.error.anonymous.no.roles=Anonyme G\u00E4ste k\u00F6nnen keine Systemrollen wahrnehmen rightsForm.isAdmin=Systemadministrator rightsForm.isAnonymous=Benutzertyp @@ -127,8 +136,12 @@ table.auth.credential=Berechtigungsnachweis table.auth.login=Benutzername table.auth.provider=Provider table.header.action=Aktion +table.header.creationDate= table.header.vcard=Visitenkarte table.identity.action=Aktion +table.identity.deletedby=Gel\u00F6scht von +table.identity.deleteddate=Gel\u00F6scht am +table.identity.deletedroles=Rolen table.identity.vcard=<i class\='o_icon o_icon-lg o_icon_home'> </i> table.identity.creationdate=Erstellt table.identity.lastlogin=letzter Login @@ -150,6 +163,7 @@ title.user.search=OLAT-Benutzer suchen title.userlist=Liste der Benutzer title.usersearch=Benutzersuche user.found=Benutzer wurde gefunden +usermanagers=Benutzerverwalter view.competences=Kompetenzen view.courses=Lernressourcen view.access=Buchungen diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties index db869fed9be..0a4c0f16883 100644 --- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties @@ -3,10 +3,13 @@ action.bulkedit=Edit these users' attributes action.choose=Choose action.choose.finish=Choose and finish action.select=Select +admins=Administrator authedit.delete.confirm=Are you sure you want to delete the authentication provider {0} for user {1}? authedit.delete.success=Authentication provider {0} deleted for user {1}. autocomplete.noresults=$org.olat.core.gui.control.generic.ajax.autocompletion\:autocomplete.noresults autocompletion.info=Please indicate an OLAT user name, a first name or a last name. +authors=Author +bgroupcoach=Group coach btn.back=Back bulkChange.failed=No modifications made bulkChange.partialsuccess={0} of {1} modification(s) successful. The following user attributes could not be modified\: {2} @@ -15,10 +18,13 @@ bulkChange.title=Modification of user attributes changeuserpwd.cancel=Action was cancelled. Password remains unchanged. changeuserpwd.failed=Your new password could not be saved. An unexpected server error occurred. changeuserpwd.successful=New password saved successfully. It is valid from now on. +clear=Clear command.mail=Send e-mail to these users +confirm.clear.identity=Do really want to clear the first name and last name of this deleted user "{0}"? content.usermgmt.text1=Here you can modify or manage data of existing OLAT users. Please start your user search. content.usermgmt.title=User management content.usermgmt.userfound=Manage user settings +deleted.user.list=List of deleted users deselectall=$org.olat.core.gui.components.table\:uncheckall edit.uauth=Authentications edit.uhomepage=Visiting card @@ -46,9 +52,10 @@ form.token.new.text=Message form.token.new.title=Send password link for OpenOLAT password form.username=User name found.property=Property selected {0} +groupmanagers=Group manager header.autocompletion=Search combined with auto-completion header.normal=Search along with user attributes - +instoresmanager=Learn resource manager mailtemplate.login.denied.body=Dear {6} {4},\n\nyour OpenOLAT account {0} with the email address {1} on system {5} has been blocked.\n\n\nIf you want to re-activate your account, please contact support at {3}\n\n\nBest regards\nThe e-learning team mailtemplate.login.denied.subject=OpenOLAT account blocked mailto.userlist=List of users @@ -72,6 +79,8 @@ new.user.cancel=Action cancelled. No new user account created. new.user.successful=The new user account has been created successfully. notification.noNews=No new users have logged on since that date. notification.noSubscription=You have not subscribed to get news about new users. +poolsmanager=Question bank manager +repocoach=Course coach rightForm.error.anonymous.no.roles=Anonymous guests cannot exercise system roles rightsForm.isAdmin=System administrator rightsForm.isAnonymous=User type @@ -129,6 +138,9 @@ table.auth.provider=Provider table.header.action=Action table.header.vcard=Visiting card table.identity.action=Action +table.identity.deletedby=Deleted by +table.identity.deleteddate=Deleted +table.identity.deletedroles=Roles table.identity.vcard=<i class\='o_icon o_icon-lg o_icon_home'> </i> table.identity.creationdate=Created table.identity.lastlogin=Last login @@ -150,6 +162,7 @@ title.user.search=Search for an OLAT user title.userlist=User list title.usersearch=User search user.found=User was found +usermanagers=User manager view.competences=Competences view.courses=Learning resources view.access=Bookings diff --git a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java index 00b48891441..dad04634125 100644 --- a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java +++ b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java @@ -113,7 +113,7 @@ public class UserBulkChangeManager implements InitializingBean { public void changeSelectedIdentities(List<Identity> selIdentities, Map<String, String> attributeChangeMap, Map<String, String> roleChangeMap, List<String> notUpdatedIdentities, boolean isAdministrativeUser, List<Long> ownGroups, List<Long> partGroups, - Translator trans, Identity addingIdentity) { + Translator trans, Identity actingIdentity) { Translator transWithFallback = userManager.getPropertyHandlerTranslator(trans); String usageIdentifyer = UserBulkChangeStep00.class.getCanonicalName(); @@ -206,12 +206,12 @@ public class UserBulkChangeManager implements InitializingBean { // user not anymore in security group, remove him if (isInGroup && thisRoleAction.equals("remove")) { securityManager.removeIdentityFromSecurityGroup(identity, secGroup); - log.audit("User::" + addingIdentity.getKey() + " removed system role::" + securityGroup + " from user::" + identity.getKey(), null); + log.audit("User::" + actingIdentity.getKey() + " removed system role::" + securityGroup + " from user::" + identity.getKey(), null); } // user not yet in security group, add him if (!isInGroup && thisRoleAction.equals("add")) { securityManager.addIdentityToSecurityGroup(identity, secGroup); - log.audit("User::" + addingIdentity.getKey() + " added system role::" + securityGroup + " to user::" + identity.getKey(), null); + log.audit("User::" + actingIdentity.getKey() + " added system role::" + securityGroup + " to user::" + identity.getKey(), null); } } } @@ -235,8 +235,8 @@ public class UserBulkChangeManager implements InitializingBean { if(oldStatus != status && status == Identity.STATUS_LOGIN_DENIED && Boolean.parseBoolean(roleChangeMap.get("sendLoginDeniedEmail"))) { sendLoginDeniedEmail(identity); } - identity = securityManager.saveIdentityStatus(identity, status); - log.audit("User::" + addingIdentity.getKey() + " changed accout status for user::" + identity.getKey() + " from::" + oldStatusText + " to::" + newStatusText, null); + identity = securityManager.saveIdentityStatus(identity, status, actingIdentity); + log.audit("User::" + actingIdentity.getKey() + " changed accout status for user::" + identity.getKey() + " from::" + oldStatusText + " to::" + newStatusText, null); } // persist changes: @@ -248,7 +248,7 @@ public class UserBulkChangeManager implements InitializingBean { userManager.updateUserFromIdentity(identity); securityManager.deleteInvalidAuthenticationsByEmail(oldEmail); changedIdentities.add(identity); - log.audit("User::" + addingIdentity.getKey() + " successfully changed account data for user::" + identity.getKey() + " in bulk change", null); + log.audit("User::" + actingIdentity.getKey() + " successfully changed account data for user::" + identity.getKey() + " in bulk change", null); } // commit changes for this user @@ -257,7 +257,7 @@ public class UserBulkChangeManager implements InitializingBean { // FXOLAT-101: add identity to new groups: if (ownGroups.size() != 0 || partGroups.size() != 0) { - List<BusinessGroupMembershipChange> changes = new ArrayList<BusinessGroupMembershipChange>(); + List<BusinessGroupMembershipChange> changes = new ArrayList<>(); for(Identity selIdentity:selIdentities) { if(ownGroups != null && !ownGroups.isEmpty()) { for(Long tutorGroupKey:ownGroups) { @@ -276,7 +276,7 @@ public class UserBulkChangeManager implements InitializingBean { } MailPackage mailing = new MailPackage(); - businessGroupService.updateMemberships(addingIdentity, changes, mailing); + businessGroupService.updateMemberships(actingIdentity, changes, mailing); dbInstance.commit(); } } diff --git a/src/main/java/org/olat/admin/user/delete/DirectDeleteController.java b/src/main/java/org/olat/admin/user/delete/DirectDeleteController.java index 425e0de4679..c67bdf8de11 100644 --- a/src/main/java/org/olat/admin/user/delete/DirectDeleteController.java +++ b/src/main/java/org/olat/admin/user/delete/DirectDeleteController.java @@ -166,7 +166,7 @@ public class DirectDeleteController extends BasicController { boolean totalSuccess = true; for (int i = 0; i < toDeleteIdentities.size(); i++) { Identity identity = toDeleteIdentities.get(i); - boolean success = userDeletionManager.deleteIdentity(identity); + boolean success = userDeletionManager.deleteIdentity(identity, getIdentity()); if (success) { dbInstance.intermediateCommit(); } else { diff --git a/src/main/java/org/olat/admin/user/delete/ReadyToDeleteController.java b/src/main/java/org/olat/admin/user/delete/ReadyToDeleteController.java index 866464fdd07..ec16d6c8c0a 100644 --- a/src/main/java/org/olat/admin/user/delete/ReadyToDeleteController.java +++ b/src/main/java/org/olat/admin/user/delete/ReadyToDeleteController.java @@ -55,6 +55,7 @@ import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.util.Util; import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; /** @@ -76,6 +77,8 @@ public class ReadyToDeleteController extends BasicController { private boolean isAdministrativeUser; private Translator propertyHandlerTranslator; + @Autowired + private UserDeletionManager userDeletionManager; /** * @param ureq @@ -178,7 +181,7 @@ public class ReadyToDeleteController extends BasicController { tableCtr = new TableController(tableConfig, ureq, getWindowControl(), this.propertyHandlerTranslator); listenTo(tableCtr); - List<Identity> l = UserDeletionManager.getInstance().getIdentitiesInDeletionProcess(UserDeletionManager.getInstance().getDeleteEmailDuration()); + List<Identity> l = userDeletionManager.getIdentitiesInDeletionProcess(UserDeletionManager.getInstance().getDeleteEmailDuration()); tdm = new UserDeleteTableModel(l, getLocale(), isAdministrativeUser); tdm.addColumnDescriptors(tableCtr, null,"table.identity.deleteEmail"); tableCtr.addColumnDescriptor(new StaticColumnDescriptor(ACTION_SINGLESELECT_CHOOSE, "table.header.action", translate("action.activate"))); @@ -197,14 +200,14 @@ public class ReadyToDeleteController extends BasicController { } protected void updateUserList() { - List<Identity> l = UserDeletionManager.getInstance().getIdentitiesReadyToDelete(UserDeletionManager.getInstance().getDeleteEmailDuration()); + List<Identity> l = userDeletionManager.getIdentitiesReadyToDelete(UserDeletionManager.getInstance().getDeleteEmailDuration()); tdm.setObjects(l); tableCtr.setTableDataModel(tdm); } private void deleteIdentities(List<Identity> identities, List<String> errors) { for (Identity id:identities) { - boolean success = UserDeletionManager.getInstance().deleteIdentity( id ); + boolean success = userDeletionManager.deleteIdentity(id, getIdentity()); if (success) { DBFactory.getInstance().intermediateCommit(); } else { diff --git a/src/main/java/org/olat/admin/user/delete/service/UserDeletionManager.java b/src/main/java/org/olat/admin/user/delete/service/UserDeletionManager.java index 1572d027f4c..54546fee46a 100644 --- a/src/main/java/org/olat/admin/user/delete/service/UserDeletionManager.java +++ b/src/main/java/org/olat/admin/user/delete/service/UserDeletionManager.java @@ -251,9 +251,9 @@ public class UserDeletionManager extends BasicManager { * Delete all user-data in registered deleteable resources. * * @param identity - * @return true: delete was successfull; false: delete could not finish + * @return true: delete was successful; false: delete could not finish */ - public boolean deleteIdentity(Identity identity) { + public boolean deleteIdentity(Identity identity, Identity doer) { logInfo("Start deleteIdentity for identity=" + identity); if(Identity.STATUS_PERMANENT.equals(identity.getStatus())) { logInfo("Aborted deletion of identity=" + identity + ", identity is flagged as PERMANENT"); @@ -261,9 +261,13 @@ public class UserDeletionManager extends BasicManager { } // Logout user and start with delete process userSessionManager.signOffAndClearAll(identity); + // set some data + identity = securityManager.saveDeletedByData(identity, doer); + dbInstance.commit(); + // Delete data of modules that implement the user data deletable - String anonymisedIdentityName = identity.getKey() + ""; + String anonymisedIdentityName = identity.getKey().toString(); File archiveFilePath = getArchivFilePath(identity); Map<String,UserDataDeletable> userDataDeletableResourcesMap = CoreSpringFactory.getBeansOfType(UserDataDeletable.class); List<UserDataDeletable> userDataDeletableResources = new ArrayList<>(userDataDeletableResourcesMap.values()); @@ -303,7 +307,7 @@ public class UserDeletionManager extends BasicManager { logInfo("Replaced username with database key for identity::" + identity.getKey()); // Finally mark user as deleted and we are done - identity = securityManager.saveIdentityStatus(identity, Identity.STATUS_DELETED); + identity = securityManager.saveIdentityStatus(identity, Identity.STATUS_DELETED, doer); logInfo("Data of identity deleted and state of identity::" + identity.getKey() + " changed to 'deleted'"); dbInstance.commit(); diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 652fd6cccf4..54be940c0d1 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -80,6 +80,15 @@ public interface BaseSecurity { */ public List<String> getRolesAsString(IdentityRef identity); + /** + * This method need several queries to catch all roles. + * + * @param identity + * @return + */ + public List<String> getRolesSummaryWithResources(IdentityRef identity); + + /** * Update the roles * @param actingIdentity The identity who is performing the change @@ -640,11 +649,14 @@ public interface BaseSecurity { SecurityGroup[] groups, PermissionOnResourceable[] permissionOnResources, String[] authProviders, Date createdAfter, Date createdBefore, Date userLoginAfter, Date userLoginBefore, Integer status); - - /** Save an identity - * @param identity Save this identity + /** + * + * @param identity The identity with a new status + * @param status The status to set + * @param doer The identity which is acting + * @return */ - public Identity saveIdentityStatus(Identity identity, Integer status); + public Identity saveIdentityStatus(Identity identity, Integer status, Identity doer); /** * Set the date of the last login @@ -669,6 +681,16 @@ public interface BaseSecurity { */ public Identity saveIdentityName(Identity identity, String newName, String newExertnalId); + /** + * the method doesn't set the status deleted, it will set the user + * who deleted the specified identity, the list of roles and the date. + * + * @param identity The identity to set the data of + * @param doer The identity which is acting + * @return The merged identity + */ + public Identity saveDeletedByData(Identity identity, Identity doer); + /** * Set an external id if the identity is managed by an external system. * diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index b9ff44ee654..1237ba72fee 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -480,6 +480,48 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable { } } + @Override + public List<String> getRolesSummaryWithResources(IdentityRef identity) { + List<String> openolatRoles = getRolesAsString(identity); + + //repository + StringBuilder sb = new StringBuilder(); + sb.append("select distinct membership.role from repositoryentry v ") + .append(" inner join v.groups as relGroup") + .append(" inner join relGroup.group as baseGroup") + .append(" inner join baseGroup.members as membership") + .append(" where membership.identity.key=:identityKey"); + List<String> repositoryRoles = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), String.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + for(String repositoryRole:repositoryRoles) { + if(repositoryRole.equals("owner")) { + openolatRoles.add(repositoryRole); + } else if(repositoryRole.equals("coach")) { + openolatRoles.add("repocoach"); + } + } + + // business groups + StringBuilder gsb = new StringBuilder(); + gsb.append("select distinct membership.role from businessgroup as bgroup ") + .append(" inner join bgroup.baseGroup as baseGroup") + .append(" inner join baseGroup.members as membership") + .append(" where membership.identity.key=:identityKey"); + List<String> groupRoles = dbInstance.getCurrentEntityManager() + .createQuery(gsb.toString(), String.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + for(String groupRole:groupRoles) { + if(groupRole.equals("coach")) { + openolatRoles.add("bgroupcoach"); + } + } + + return openolatRoles; + } + /** * scalar query : select sgi, poi, ori * @param identity @@ -2067,18 +2109,61 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable { return string; } - /** - * @see org.olat.basesecurity.Manager#saveIdentityStatus(org.olat.core.id.Identity) - */ @Override - public Identity saveIdentityStatus(Identity identity, Integer status) { - IdentityImpl reloadedIdentity = loadForUpdate(identity); - reloadedIdentity.setStatus(status); - reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); - dbInstance.commit(); + public Identity saveIdentityStatus(Identity identity, Integer status, Identity doer) { + IdentityImpl reloadedIdentity = loadForUpdate(identity); + if(reloadedIdentity != null) { + reloadedIdentity.setStatus(status); + if(status.equals(Identity.STATUS_DELETED)) { + if(doer != null && reloadedIdentity.getDeletedBy() == null) { + reloadedIdentity.setDeletedBy(getDeletedByName(doer)); + } + reloadedIdentity.setDeletedDate(new Date()); + } + reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); + dbInstance.commit(); + } + return reloadedIdentity; + } + + @Override + public Identity saveDeletedByData(Identity identity, Identity doer) { + IdentityImpl reloadedIdentity = loadForUpdate(identity); + if(reloadedIdentity != null) { + reloadedIdentity.setDeletedBy(getDeletedByName(doer)); + reloadedIdentity.setDeletedDate(new Date()); + + List<String> deletedRoles = getRolesSummaryWithResources(reloadedIdentity); + StringBuilder deletedRoleBuffer = new StringBuilder(); + for(String deletedRole:deletedRoles) { + if(deletedRoleBuffer.length() > 0) deletedRoleBuffer.append(","); + deletedRoleBuffer.append(deletedRole); + } + + reloadedIdentity.setDeletedRoles(deletedRoleBuffer.toString()); + reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); + dbInstance.commit(); + } return reloadedIdentity; } + private String getDeletedByName(Identity doer) { + StringBuilder sb = new StringBuilder(128); + if(doer != null) { + if(StringHelper.containsNonWhitespace(doer.getUser().getLastName())) { + sb.append(doer.getUser().getLastName()); + } + if(StringHelper.containsNonWhitespace(doer.getUser().getFirstName())) { + if(sb.length() > 0) sb.append(", "); + sb.append(doer.getUser().getFirstName()); + } + } + if(sb.length() > 128) { + sb.delete(128, sb.length()); + } + return sb.toString(); + } + @Override public void setIdentityLastLogin(IdentityRef identity) { dbInstance.getCurrentEntityManager() @@ -2092,19 +2177,23 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable { @Override public Identity saveIdentityName(Identity identity, String newName, String newExternalId) { IdentityImpl reloadedIdentity = loadForUpdate(identity); - reloadedIdentity.setName(newName); - reloadedIdentity.setExternalId(newExternalId); - reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); - dbInstance.commit(); + if(reloadedIdentity != null) { + reloadedIdentity.setName(newName); + reloadedIdentity.setExternalId(newExternalId); + reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); + dbInstance.commit(); + } return reloadedIdentity; } @Override public Identity setExternalId(Identity identity, String externalId) { - IdentityImpl reloadedIdentity = loadForUpdate(identity); - reloadedIdentity.setExternalId(externalId); - reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); - dbInstance.commit(); + IdentityImpl reloadedIdentity = loadForUpdate(identity); + if(reloadedIdentity != null) { + reloadedIdentity.setExternalId(externalId); + reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); + dbInstance.commit(); + } return reloadedIdentity; } diff --git a/src/main/java/org/olat/basesecurity/DeletedIdentitiesQueries.java b/src/main/java/org/olat/basesecurity/DeletedIdentitiesQueries.java new file mode 100644 index 00000000000..5d0eb85abd7 --- /dev/null +++ b/src/main/java/org/olat/basesecurity/DeletedIdentitiesQueries.java @@ -0,0 +1,41 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.basesecurity; + +import java.util.List; + +import org.olat.basesecurity.model.DeletedIdentity; +import org.olat.core.commons.persistence.SortKey; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public interface DeletedIdentitiesQueries { + + public int countDeletedIdentities(); + + public List<DeletedIdentity> getIdentitiesByPowerSearch(int firstResult, int maxResults, SortKey orderBy); + + + +} diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml index 00427282f96..cf9c93b2691 100644 --- a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml +++ b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml @@ -18,15 +18,14 @@ <property name="creationDate" column="creationdate" type="timestamp" /> <property name="lastLogin" column="lastlogin" update="false" type="timestamp" /> <property name="externalId" column="external_id" type="string" /> - - <property name="name" type="string"> - <column name="name" unique="true" length="128" not-null="true" index="name_idx2" /> - </property> - <property name="status" type="integer"> - <column name="status" index="identstatus_idx"/> - </property> - + <property name="name" column="name" type="string" unique="true" not-null="true" /> + <property name="status" column="status" type="integer" /> + <property name="deletedDate" column="deleteddate" type="timestamp" /> + <property name="deletedRoles" column="deletedroles" type="string" /> + <property name="deletedBy" column="deletedby" type="string" /> + <one-to-one name="user" property-ref="identity" class="org.olat.user.UserImpl" cascade="persist"/> + </class> diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.java b/src/main/java/org/olat/basesecurity/IdentityImpl.java index 62805f7a100..54f6a914e82 100644 --- a/src/main/java/org/olat/basesecurity/IdentityImpl.java +++ b/src/main/java/org/olat/basesecurity/IdentityImpl.java @@ -56,6 +56,10 @@ public class IdentityImpl implements Identity, IdentityRef, CreateInfo, Persista /** status=[activ|deleted|permanent] */ private int status; + private Date deletedDate; + private String deletedBy; + private String deletedRoles; + /** * Maximum length of an identity's name. */ @@ -162,6 +166,30 @@ public class IdentityImpl implements Identity, IdentityRef, CreateInfo, Persista this.status = status == null ? 0 : status.intValue(); } + public Date getDeletedDate() { + return deletedDate; + } + + public void setDeletedDate(Date deletedDate) { + this.deletedDate = deletedDate; + } + + public String getDeletedRoles() { + return deletedRoles; + } + + public void setDeletedRoles(String deletedRoles) { + this.deletedRoles = deletedRoles; + } + + public String getDeletedBy() { + return deletedBy; + } + + public void setDeletedBy(String deletedBy) { + this.deletedBy = deletedBy; + } + @Override public int hashCode() { int hash = 7; diff --git a/src/main/java/org/olat/basesecurity/manager/DeletedIdentitiesQueriesImpl.java b/src/main/java/org/olat/basesecurity/manager/DeletedIdentitiesQueriesImpl.java new file mode 100644 index 00000000000..f75cf4e9517 --- /dev/null +++ b/src/main/java/org/olat/basesecurity/manager/DeletedIdentitiesQueriesImpl.java @@ -0,0 +1,130 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.basesecurity.manager; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.olat.basesecurity.DeletedIdentitiesQueries; +import org.olat.basesecurity.IdentityImpl; +import org.olat.basesecurity.model.DeletedIdentity; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.SortKey; +import org.olat.core.id.Identity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class DeletedIdentitiesQueriesImpl implements DeletedIdentitiesQueries { + + @Autowired + private DB dbInstance; + + @Override + public int countDeletedIdentities() { + StringBuilder sb = new StringBuilder(500); + sb.append("select count(ident.key) from ").append(IdentityImpl.class.getCanonicalName()).append(" as ident ") + .append(" where ident.status=:status"); + + List<Long> count = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) + .setParameter("status", Identity.STATUS_DELETED) + .getResultList(); + return count == null || count.isEmpty() || count.get(0) == null ? 0 : count.get(0).intValue(); + } + + @Override + public List<DeletedIdentity> getIdentitiesByPowerSearch(int firstResult, int maxResults, SortKey orderBy) { + // username, firstname, lastname, deleted date, last login date, created date, deletedby, deletedRoles + + StringBuilder sb = new StringBuilder(500); + sb.append("select ident.key, ident.name, ident.deletedDate, ident.lastLogin,") + .append(" ident.creationDate, ident.deletedRoles, ident.deletedBy,") + .append(" identUser.firstName, identUser.lastName") + .append(" from ").append(IdentityImpl.class.getCanonicalName()).append(" as ident ") + .append(" left join ident.user as identUser") + .append(" where ident.status=:status"); + orderBy(sb, orderBy); + + List<Object[]> rawObjects = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Object[].class) + .setParameter("status", Identity.STATUS_DELETED) + .setFirstResult(firstResult) + .setMaxResults(maxResults) + .getResultList(); + + List<DeletedIdentity> identities = new ArrayList<>(rawObjects.size()); + for(Object[] rawObject:rawObjects) { + int pos = 0; + Long identityKey = (Long)rawObject[pos++]; + String identityName = (String)rawObject[pos++]; + Date deletedDate = (Date)rawObject[pos++]; + Date lastLogin = (Date)rawObject[pos++]; + Date creationDate = (Date)rawObject[pos++]; + String deletedRoles = (String)rawObject[pos++]; + String deletedBy = (String)rawObject[pos++]; + + String identityFirstName = (String)rawObject[pos++]; + String identityLastName = (String)rawObject[pos++]; + + identities.add(new DeletedIdentity(identityKey, identityName, identityFirstName, identityLastName, + deletedDate, lastLogin, creationDate, deletedRoles, deletedBy)); + } + return identities; + } + + private void orderBy(StringBuilder sb, SortKey orderBy) { + if(orderBy == null) return; + + switch(orderBy.getKey()) { + case "username": + sb.append(" order by ident.name ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "creationDate": + sb.append(" order by ident.creationDate ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "lastLogin": + sb.append(" order by ident.lastLogin ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "deletedDate": + sb.append(" order by ident.deletedDate ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "firstName": + sb.append(" order by lower(identUser.firstName), lower(identUser.lastName) ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "lastName": + sb.append(" order by lower(identUser.lastName), lower(identUser.firstName) ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + case "deletedBy": + sb.append(" order by lower(ident.deletedBy), lower(identUser.lastName) ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + default: + sb.append(" order by ident.key ").append(orderBy.isAsc() ? "asc" : "desc"); + break; + } + } +} diff --git a/src/main/java/org/olat/basesecurity/model/DeletedIdentity.java b/src/main/java/org/olat/basesecurity/model/DeletedIdentity.java new file mode 100644 index 00000000000..127e5e9f370 --- /dev/null +++ b/src/main/java/org/olat/basesecurity/model/DeletedIdentity.java @@ -0,0 +1,91 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.basesecurity.model; + +import java.util.Date; + +/** + * + * Initial date: 22 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DeletedIdentity { + + private final Long identityKey; + private final String identityName; + private final String identityFirstName; + private final String identityLastName; + + private final Date deletedDate; + private final Date lastLogin; + private final Date creationDate; + private final String deletedRoles; + private final String deletedBy; + + public DeletedIdentity(Long identityKey, String identityName, String identityFirstName, String identityLastName, + Date deletedDate, Date lastLogin, Date creationDate, String deletedRoles, String deletedBy) { + this.identityKey = identityKey; + this.identityName = identityName; + this.identityFirstName = identityFirstName; + this.identityLastName = identityLastName; + this.deletedDate = deletedDate; + this.lastLogin = lastLogin; + this.creationDate = creationDate; + this.deletedRoles = deletedRoles; + this.deletedBy = deletedBy; + } + + public Long getIdentityKey() { + return identityKey; + } + + public String getIdentityName() { + return identityName; + } + + public String getIdentityFirstName() { + return identityFirstName; + } + + public String getIdentityLastName() { + return identityLastName; + } + + public Date getDeletedDate() { + return deletedDate; + } + + public Date getLastLogin() { + return lastLogin; + } + + public Date getCreationDate() { + return creationDate; + } + + public String getDeletedRoles() { + return deletedRoles; + } + + public String getDeletedBy() { + return deletedBy; + } +} diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/DefaultFlexiColumnModel.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/DefaultFlexiColumnModel.java index 518dd8cea3d..bfdff096e73 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/DefaultFlexiColumnModel.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/DefaultFlexiColumnModel.java @@ -73,6 +73,11 @@ public class DefaultFlexiColumnModel implements FlexiColumnModel { new TextFlexiCellRenderer()); } + public DefaultFlexiColumnModel(boolean defVisible, FlexiSortableColumnDef def, FlexiCellRenderer renderer) { + this(defVisible, false, def.i18nHeaderKey(), def.ordinal(), null, def.sortable(), def.sortKey(), FlexiColumnModel.ALIGNMENT_LEFT, + renderer); + } + public DefaultFlexiColumnModel(FlexiSortableColumnDef def, String action) { this(true, false, def.i18nHeaderKey(), def.ordinal(), action, def.sortable(), def.sortKey(), FlexiColumnModel.ALIGNMENT_LEFT, new StaticFlexiCellRenderer(action, new TextFlexiCellRenderer())); diff --git a/src/main/java/org/olat/ldap/LDAPLoginManager.java b/src/main/java/org/olat/ldap/LDAPLoginManager.java index 2faaa37e0c5..eaa506bf01e 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginManager.java +++ b/src/main/java/org/olat/ldap/LDAPLoginManager.java @@ -59,7 +59,7 @@ public interface LDAPLoginManager { public void syncUserGroups(Identity identity); - public void deletIdentities(List<Identity> identityList); + public void deleteIdentities(List<Identity> identityList, Identity doer); public boolean doBatchSync(LDAPError errors); diff --git a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java index acde9ae16af..f43d5316227 100644 --- a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java +++ b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java @@ -428,7 +428,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe * @param identityList List of Identities to delete */ @Override - public void deletIdentities(List<Identity> identityList) { + public void deleteIdentities(List<Identity> identityList, Identity doer) { SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); for (Identity identity: identityList) { @@ -438,7 +438,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe } securityManager.removeIdentityFromSecurityGroup(identity, secGroup); - userDeletionManager.deleteIdentity(identity); + userDeletionManager.deleteIdentity(identity, doer); dbInstance.intermediateCommit(); } } @@ -1125,7 +1125,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListe + "% tried to delete."); } else { // delete users - deletIdentities(deletedUserList); + deleteIdentities(deletedUserList, null); log.info("LDAP batch sync: " + deletedUserList.size() + " users deleted" + sinceSentence); diff --git a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java index 2b11f3440b9..afa7b1f910f 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java @@ -204,7 +204,7 @@ public class LDAPAdminController extends BasicController implements GenericEvent amountUsersToDelete = idToDelete.size(); // Delete all identities now and tell everybody that // we are finished - ldapLoginManager.deletIdentities(idToDelete); + ldapLoginManager.deleteIdentities(idToDelete, getIdentity()); return StepsMainRunController.DONE_MODIFIED; } else { return StepsMainRunController.DONE_UNCHANGED; diff --git a/src/main/java/org/olat/modules/portfolio/ui/AbstractPageListController.java b/src/main/java/org/olat/modules/portfolio/ui/AbstractPageListController.java index 674bbf8e39b..17f7b92c2d0 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/AbstractPageListController.java +++ b/src/main/java/org/olat/modules/portfolio/ui/AbstractPageListController.java @@ -211,7 +211,7 @@ implements Activateable2, TooledController, FlexiTableComponentDelegate { } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PageCols.publicationDate, "select-page")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PageCols.categories, new CategoriesCellRenderer())); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PageCols.section/*, "select-section"*/, null)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PageCols.section/*, "select-section"*/)); if(secCallback.canNewAssignment()) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.up", PageCols.up.ordinal(), "up", new BooleanCellRenderer( diff --git a/src/main/java/org/olat/portfolio/manager/InvitationDAO.java b/src/main/java/org/olat/portfolio/manager/InvitationDAO.java index cfba3cf6a7a..e37a30d780f 100644 --- a/src/main/java/org/olat/portfolio/manager/InvitationDAO.java +++ b/src/main/java/org/olat/portfolio/manager/InvitationDAO.java @@ -66,6 +66,8 @@ public class InvitationDAO { private UserManager userManager; @Autowired private BaseSecurity securityManager; + @Autowired + private UserDeletionManager userDeletionManager; public Invitation createInvitation() { InvitationImpl invitation = new InvitationImpl(); @@ -336,7 +338,7 @@ public class InvitationDAO { //out of scope } else { //delete user - UserDeletionManager.getInstance().deleteIdentity(identity); + userDeletionManager.deleteIdentity(identity, null); } } Invitation invitationRef = dbInstance.getCurrentEntityManager() diff --git a/src/main/java/org/olat/user/UserManager.java b/src/main/java/org/olat/user/UserManager.java index 1fe345bd48a..2d5da2664b2 100644 --- a/src/main/java/org/olat/user/UserManager.java +++ b/src/main/java/org/olat/user/UserManager.java @@ -178,6 +178,8 @@ public abstract class UserManager extends BasicManager { * @return true if successful. */ public abstract boolean updateUserFromIdentity(Identity identity); + + public abstract void clearAllUserProperties(Identity identity); /** * Saves or updates the stringValue of the user's charset property diff --git a/src/main/java/org/olat/user/UserManagerImpl.java b/src/main/java/org/olat/user/UserManagerImpl.java index 80828bb9dd1..c70df6bc9e8 100644 --- a/src/main/java/org/olat/user/UserManagerImpl.java +++ b/src/main/java/org/olat/user/UserManagerImpl.java @@ -36,6 +36,8 @@ import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.Constants; +import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.IdentityImpl; import org.olat.basesecurity.IdentityNames; import org.olat.basesecurity.IdentityRef; @@ -551,8 +553,7 @@ public class UserManagerImpl extends UserManager implements UserDataDeletable { private String getDomain() { AuthenticationProvider authenticationProvider = loginModule.getAuthenticationProvider("OLAT"); String issuer = authenticationProvider.getIssuerIdentifier(null); - String domain = issuer.startsWith("https://")? issuer.substring(8): issuer; - return domain; + return issuer.startsWith("https://")? issuer.substring(8): issuer; } @Override @@ -564,20 +565,46 @@ public class UserManagerImpl extends UserManager implements UserDataDeletable { @Override public void deleteUserData(Identity identity, String newDeletedUserName, File archivePath) { identity = securityManager.loadIdentityByKey(identity.getKey()); + + String roles = ((IdentityImpl)identity).getDeletedRoles(); + boolean isAdministrativeUser = roles != null && (roles.contains(Constants.GROUP_ADMIN) || roles.contains(Constants.GROUP_AUTHORS) + || roles.contains(Constants.GROUP_GROUPMANAGERS) || roles.contains(Constants.GROUP_INST_ORES_MANAGER) + || roles.contains(Constants.GROUP_OWNERS) || roles.contains(GroupRoles.owner.name()) + || roles.contains(Constants.GROUP_USERMANAGERS) || roles.contains(Constants.GROUP_POOL_MANAGER)); + User persistedUser = identity.getUser(); - List<UserPropertyHandler> userPropertyHandlers = this.getAllUserPropertyHandlers(); + List<UserPropertyHandler> userPropertyHandlers = getAllUserPropertyHandlers(); for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { String actualProperty = userPropertyHandler.getName(); if (UserConstants.USERNAME.equals(actualProperty)) { // Skip, user name will be anonymised by BaseSecurityManager + } else if(isAdministrativeUser && (UserConstants.FIRSTNAME.equals(actualProperty) || UserConstants.LASTNAME.equals(actualProperty))) { + // Skip first name and last name of user with administrative functions } else { persistedUser.setProperty(actualProperty, null); logDebug("Deleted user-property::" + actualProperty + " for identity::+" + identity.getKey()); } } - this.updateUserFromIdentity(identity); + updateUserFromIdentity(identity); logInfo("deleteUserProperties user::" + persistedUser.getKey() + " from identity::" + identity.getKey()); dbInstance.commit(); } + @Override + public void clearAllUserProperties(Identity identity) { + User persistedUser = identity.getUser(); + List<UserPropertyHandler> userPropertyHandlers = getAllUserPropertyHandlers(); + for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { + String actualProperty = userPropertyHandler.getName(); + if (UserConstants.USERNAME.equals(actualProperty)) { + // Skip, user name will be anonymised by BaseSecurityManager + } else { + persistedUser.setProperty(actualProperty, null); + logDebug("Deleted user-property::" + actualProperty + " for identity::+" + identity.getKey()); + } + } + updateUserFromIdentity(identity); + logInfo("clearUserProperties user::" + persistedUser.getKey() + " from identity::" + identity.getKey()); + dbInstance.commit(); + } } diff --git a/src/main/java/org/olat/user/restapi/UserWebService.java b/src/main/java/org/olat/user/restapi/UserWebService.java index 71328bb387f..a0d7a058485 100644 --- a/src/main/java/org/olat/user/restapi/UserWebService.java +++ b/src/main/java/org/olat/user/restapi/UserWebService.java @@ -408,8 +408,9 @@ public class UserWebService { @Produces({MediaType.APPLICATION_XML ,MediaType.APPLICATION_JSON}) public Response updateStatus(@PathParam("identityKey") Long identityKey, StatusVO status, @Context HttpServletRequest request) { try { + Identity actingIdentity = getIdentity(request); boolean isUserManager = isUserManager(request); - if(!isUserManager) { + if(actingIdentity == null || !isUserManager) { return Response.serverError().status(Status.FORBIDDEN).build(); } Identity identity = BaseSecurityManager.getInstance().loadIdentityByKey(identityKey, false); @@ -418,7 +419,7 @@ public class UserWebService { } Integer newStatus = status.getStatus(); - identity = BaseSecurityManager.getInstance().saveIdentityStatus(identity, newStatus); + identity = BaseSecurityManager.getInstance().saveIdentityStatus(identity, newStatus, actingIdentity); StatusVO reloadedStatus = new StatusVO(); reloadedStatus.setStatus(identity.getStatus()); return Response.ok(reloadedStatus).build(); @@ -870,7 +871,8 @@ public class UserWebService { @DELETE @Path("{identityKey}") public Response delete(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { - if(!isUserManager(request)) { + Identity actingIdentity = getIdentity(request); + if(actingIdentity == null || !isUserManager(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -878,7 +880,7 @@ public class UserWebService { if(identity == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } - boolean success = UserDeletionManager.getInstance().deleteIdentity(identity); + boolean success = UserDeletionManager.getInstance().deleteIdentity(identity, actingIdentity); if (success) { return Response.ok().build(); } else { diff --git a/src/main/resources/database/mysql/alter_12_4_x_to_12_5_0.sql b/src/main/resources/database/mysql/alter_12_4_x_to_12_5_0.sql index 6b530fca0c2..36c1baa887b 100644 --- a/src/main/resources/database/mysql/alter_12_4_x_to_12_5_0.sql +++ b/src/main/resources/database/mysql/alter_12_4_x_to_12_5_0.sql @@ -1,3 +1,8 @@ +alter table o_bs_identity add column deleteddate datetime; +alter table o_bs_identity add column deletedroles varchar(1024); +alter table o_bs_identity add column deletedby varchar(128); + + alter table o_loggingtable drop username, drop userproperty1, drop userproperty2, drop userproperty3, drop userproperty4, drop userproperty5, drop userproperty6, drop userproperty7, drop userproperty8, drop userproperty9, drop userproperty10, drop userproperty11, drop userproperty12; update o_bs_identity set name=id where status=199; diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 61b07f11bf0..fb1476a684f 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -168,6 +168,9 @@ create table if not exists o_bs_identity ( name varchar(128) not null unique, external_id varchar(64), status integer, + deleteddate datetime, + deletedroles varchar(1024), + deletedby varchar(128), primary key (id) ); create table o_csp_log ( diff --git a/src/main/resources/database/oracle/alter_12_4_x_to_12_5_0.sql b/src/main/resources/database/oracle/alter_12_4_x_to_12_5_0.sql index 89a8074b5fb..863a3fd6e08 100644 --- a/src/main/resources/database/oracle/alter_12_4_x_to_12_5_0.sql +++ b/src/main/resources/database/oracle/alter_12_4_x_to_12_5_0.sql @@ -1,3 +1,8 @@ +alter table o_bs_identity add deleteddate date; +alter table o_bs_identity add deletedroles varchar(1024); +alter table o_bs_identity add deletedby varchar(128); + + alter table o_loggingtable drop (username, userproperty1, userproperty2, userproperty3, userproperty4, userproperty5, userproperty6, userproperty7, userproperty8, userproperty9, userproperty10, userproperty11, userproperty12); update o_bs_identity set name=id where status=199; diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index 4f65201b124..997a5689b52 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -186,6 +186,9 @@ CREATE TABLE o_bs_identity ( name varchar2(128 char) NOT NULL, external_id varchar2(64 char), status number(11), + deleteddate date, + deletedroles varchar(1024), + deletedby varchar(128), CONSTRAINT u_o_bs_identity UNIQUE (name), PRIMARY KEY (id) ); diff --git a/src/main/resources/database/postgresql/alter_12_4_x_to_12_5_0.sql b/src/main/resources/database/postgresql/alter_12_4_x_to_12_5_0.sql index 1745ccee7d6..59968f8bfe0 100644 --- a/src/main/resources/database/postgresql/alter_12_4_x_to_12_5_0.sql +++ b/src/main/resources/database/postgresql/alter_12_4_x_to_12_5_0.sql @@ -1,3 +1,8 @@ +alter table o_bs_identity add column deleteddate timestamp; +alter table o_bs_identity add column deletedroles varchar(1024); +alter table o_bs_identity add column deletedby varchar(128); + + alter table o_loggingtable drop column username, drop column userproperty1, drop column userproperty2, drop column userproperty3, drop column userproperty4, drop column userproperty5, drop column userproperty6, drop column userproperty7, drop column userproperty8, drop column userproperty9, drop column userproperty10, drop column userproperty11, drop column userproperty12; update o_bs_identity set name=id where status=199; diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 1b33e5df25b..4b49dcb8db4 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -166,6 +166,9 @@ create table o_bs_identity ( name varchar(128) not null unique, external_id varchar(64), status integer, + deleteddate timestamp, + deletedroles varchar(1024), + deletedby varchar(128), primary key (id) ); create table o_csp_log ( diff --git a/src/test/java/org/olat/admin/user/delete/service/UserDeletionManagerTest.java b/src/test/java/org/olat/admin/user/delete/service/UserDeletionManagerTest.java index feb8f09a894..1b42d6fd96a 100644 --- a/src/test/java/org/olat/admin/user/delete/service/UserDeletionManagerTest.java +++ b/src/test/java/org/olat/admin/user/delete/service/UserDeletionManagerTest.java @@ -29,6 +29,7 @@ package org.olat.admin.user.delete.service; import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -39,9 +40,11 @@ import org.junit.Test; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.GroupRoles; +import org.olat.basesecurity.IdentityImpl; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; +import org.olat.core.id.Roles; import org.olat.core.id.User; import org.olat.core.id.UserConstants; import org.olat.core.util.StringHelper; @@ -60,6 +63,7 @@ import org.olat.test.OlatTestCase; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; + /** * Description: <br> * @@ -85,7 +89,7 @@ public class UserDeletionManagerTest extends OlatTestCase { private BusinessGroupService businessGroupService; @Test - public void testDeleteIdentity() { + public void deleteIdentity() { String username = "id-to-del-" + UUID.randomUUID(); String email = username + "@frentix.com"; User user = userManager.createUser("first" + username, "last" + username, email); @@ -118,7 +122,7 @@ public class UserDeletionManagerTest extends OlatTestCase { Assert.assertTrue(repositoryService.hasRole(identity, false, GroupRoles.owner.name())); //delete the identity - userDeletionManager.deleteIdentity(identity); + userDeletionManager.deleteIdentity(identity, null); dbInstance.commit(); //check @@ -145,6 +149,10 @@ public class UserDeletionManagerTest extends OlatTestCase { Assert.assertFalse(isOwner); User deletedUser = deletedIdentity.getUser(); + // process keep first name last name from user with some "administrative" + Assert.assertEquals("first" + username, deletedUser.getProperty(UserConstants.FIRSTNAME, null)); + Assert.assertEquals("last" + username, deletedUser.getProperty(UserConstants.LASTNAME, null)); + // but not the other properties String institutionalName = deletedUser.getProperty(UserConstants.INSTITUTIONALNAME, null); Assert.assertFalse(StringHelper.containsNonWhitespace(institutionalName)); String institutionalId = deletedUser.getProperty(UserConstants.INSTITUTIONALUSERIDENTIFIER, null); @@ -152,9 +160,52 @@ public class UserDeletionManagerTest extends OlatTestCase { String deletedEmail = deletedUser.getProperty(UserConstants.EMAIL, null); Assert.assertFalse(StringHelper.containsNonWhitespace(deletedEmail)); } + + + /** + * The test checked that all of the user properties are wiped out. + * + */ + @Test + public void deleteIdentity_noRoles() { + Identity groupCoach = JunitTestHelper.createAndPersistIdentityAsRndUser("del-6"); + + String username = "id-to-del-2-" + UUID.randomUUID(); + String email = username + "@frentix.com"; + User user = userManager.createUser("first" + username, "last" + username, email); + user.setProperty(UserConstants.COUNTRY, ""); + user.setProperty(UserConstants.CITY, "Basel"); + user.setProperty(UserConstants.INSTITUTIONALNAME, "Del-23"); + user.setProperty(UserConstants.INSTITUTIONALUSERIDENTIFIER, "Del-24"); + Identity identity = securityManager.createAndPersistIdentityAndUser(username, null, user, BaseSecurityModule.getDefaultAuthProviderIdentifier(), username, "secret"); + dbInstance.commitAndCloseSession(); + + //a group + Roles coachRolesId = securityManager.getRoles(groupCoach); + BusinessGroup group = businessGroupService.createBusinessGroup(groupCoach, "Group", "Group", -1, -1, false, false, null); + dbInstance.commit(); + businessGroupService.addParticipants(groupCoach, coachRolesId, Collections.singletonList(identity), group, null); + dbInstance.commit(); + + //delete the identity + userDeletionManager.deleteIdentity(identity, groupCoach); + dbInstance.commit(); + + IdentityImpl deletedIdentity = (IdentityImpl)securityManager.loadIdentityByKey(identity.getKey()); + Assert.assertNotNull(deletedIdentity); + Assert.assertNotNull(deletedIdentity.getDeletedDate()); + Assert.assertEquals(groupCoach.getUser().getLastName() + ", " + groupCoach.getUser().getFirstName(), deletedIdentity.getDeletedBy()); + + User deletedUser = deletedIdentity.getUser(); + Assert.assertFalse(StringHelper.containsNonWhitespace(deletedUser.getProperty(UserConstants.FIRSTNAME, null))); + Assert.assertFalse(StringHelper.containsNonWhitespace(deletedUser.getProperty(UserConstants.LASTNAME, null))); + Assert.assertFalse(StringHelper.containsNonWhitespace(deletedUser.getProperty(UserConstants.INSTITUTIONALNAME, null))); + Assert.assertFalse(StringHelper.containsNonWhitespace(deletedUser.getProperty(UserConstants.INSTITUTIONALUSERIDENTIFIER, null))); + Assert.assertFalse(StringHelper.containsNonWhitespace(deletedUser.getProperty(UserConstants.EMAIL, null))); + } @Test - public void testSetIdentityAsActiv() throws InterruptedException { + public void setIdentityAsActiv() throws InterruptedException { Identity ident = JunitTestHelper.createAndPersistIdentityAsUser("anIdentity"); final int maxLoop = 2000; // => 2000 x 11ms => 22sec => finished in 120sec diff --git a/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java b/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java index 3d6ea1e2916..3ff888433d6 100644 --- a/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java +++ b/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java @@ -71,7 +71,7 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase { Identity uniIdent = getOrCreateTestIdentity("extremegroovy-" + suffix); Assert.assertNotNull(uniIdent); Identity deletedIdent = getOrCreateTestIdentity("delete-" + suffix); - deletedIdent = baseSecurityManager.saveIdentityStatus(deletedIdent, Identity.STATUS_DELETED); + deletedIdent = baseSecurityManager.saveIdentityStatus(deletedIdent, Identity.STATUS_DELETED, null); SecurityGroup admins = baseSecurityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); baseSecurityManager.addIdentityToSecurityGroup(deletedIdent, admins); @@ -118,7 +118,7 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase { Identity uniIdent = getOrCreateTestIdentity("extremegroovy-" + suffix); Assert.assertNotNull(uniIdent); Identity deletedIdent = getOrCreateTestIdentity("delete-" + suffix); - deletedIdent = baseSecurityManager.saveIdentityStatus(deletedIdent, Identity.STATUS_DELETED); + deletedIdent = baseSecurityManager.saveIdentityStatus(deletedIdent, Identity.STATUS_DELETED, null); SecurityGroup admins = baseSecurityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); baseSecurityManager.addIdentityToSecurityGroup(deletedIdent, admins); @@ -210,8 +210,8 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase { Identity ident2 = getOrCreateTestIdentity("extremegroovy-" + suffix); // add some stats - ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV); - ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV); + ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV, null); + ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV, null); // check on those four default groups SecurityGroup admins = baseSecurityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); @@ -275,8 +275,8 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase { Identity ident2 = getOrCreateTestIdentity("extremegroovy-" + suffix); // add some stats - ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV); - ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV); + ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV, null); + ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV, null); // check on those four default groups SecurityGroup admins = baseSecurityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); @@ -346,8 +346,8 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase { Identity ident2 = getOrCreateTestIdentity("extremegroovy-" + suffix); // add some stats - ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV); - ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV); + ident = baseSecurityManager.saveIdentityStatus(ident, Identity.STATUS_ACTIV, null); + ident2 = baseSecurityManager.saveIdentityStatus(ident2, Identity.STATUS_ACTIV, null); // check on those four default groups SecurityGroup admins = baseSecurityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); diff --git a/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAOTest.java b/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAOTest.java index fcaa72e9406..71c5ac2a2cf 100644 --- a/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAOTest.java +++ b/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAOTest.java @@ -163,7 +163,7 @@ public class UserCommentsDAOTest extends OlatTestCase { dbInstance.commitAndCloseSession(); // delete the first user - userDeletionManager.deleteIdentity(identToDelete); + userDeletionManager.deleteIdentity(identToDelete, null); dbInstance.commitAndCloseSession(); // delete comments from first identity, and replace the comment if it has a reply diff --git a/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAOTest.java b/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAOTest.java index 5c1dcd8ce7f..3e176cff367 100644 --- a/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAOTest.java +++ b/src/test/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAOTest.java @@ -205,7 +205,7 @@ public class UserRatingsDAOTest extends OlatTestCase { Assert.assertEquals(3.0f, courseAverage, 0.001f); // delete first user - userDeletionManager.deleteIdentity(identToDelete); + userDeletionManager.deleteIdentity(identToDelete, null); dbInstance.commitAndCloseSession(); //check that rating of the first user are deleted diff --git a/src/test/java/org/olat/instantMessaging/InstantMessageServiceTest.java b/src/test/java/org/olat/instantMessaging/InstantMessageServiceTest.java index 73e7651251f..9c46b43a022 100644 --- a/src/test/java/org/olat/instantMessaging/InstantMessageServiceTest.java +++ b/src/test/java/org/olat/instantMessaging/InstantMessageServiceTest.java @@ -175,7 +175,7 @@ public class InstantMessageServiceTest extends OlatTestCase { Assert.assertNotNull(message); // delete the user - userDeletionManager.deleteIdentity(chatter1); + userDeletionManager.deleteIdentity(chatter1, null); dbInstance.commitAndCloseSession(); // check preferences are deleted diff --git a/src/test/java/org/olat/ldap/LDAPLoginTest.java b/src/test/java/org/olat/ldap/LDAPLoginTest.java index 67811727283..82c9f9502ca 100644 --- a/src/test/java/org/olat/ldap/LDAPLoginTest.java +++ b/src/test/java/org/olat/ldap/LDAPLoginTest.java @@ -207,7 +207,7 @@ public class LDAPLoginTest extends OlatTestCase { //delete user in OLAT securityManager.removeIdentityFromSecurityGroup(identity, secGroup1); - UserDeletionManager.getInstance().deleteIdentity(identity); + UserDeletionManager.getInstance().deleteIdentity(identity, null); // simulate closed session DBFactory.getInstance().intermediateCommit(); @@ -373,7 +373,7 @@ public class LDAPLoginTest extends OlatTestCase { } //delete all users - ldapManager.deletIdentities(deletedUserList); + ldapManager.deleteIdentities(deletedUserList, null); //check if users are deleted deletedUserList = ldapManager.getIdentitysDeletedInLdap(ctx); diff --git a/src/test/java/org/olat/modules/portfolio/manager/PortfolioServiceTest.java b/src/test/java/org/olat/modules/portfolio/manager/PortfolioServiceTest.java index e4f1dc76a5c..2524060b2f2 100644 --- a/src/test/java/org/olat/modules/portfolio/manager/PortfolioServiceTest.java +++ b/src/test/java/org/olat/modules/portfolio/manager/PortfolioServiceTest.java @@ -1183,7 +1183,7 @@ public class PortfolioServiceTest extends OlatTestCase { dbInstance.commitAndCloseSession(); // delete the user - userDeletionManager.deleteIdentity(owner); + userDeletionManager.deleteIdentity(owner, null); // the template is a learn ressource and will not be deleted Binder reloadedtemplateBinder = portfolioService.getBinderByKey(templateBinder.getKey()); diff --git a/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java b/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java index ac8253425f1..b9417e6027d 100644 --- a/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java +++ b/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java @@ -1,3 +1,22 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ package org.olat.restapi; import java.io.File; diff --git a/src/test/java/org/olat/user/UserDAOTest.java b/src/test/java/org/olat/user/UserDAOTest.java index 297f615257e..45b4279febf 100644 --- a/src/test/java/org/olat/user/UserDAOTest.java +++ b/src/test/java/org/olat/user/UserDAOTest.java @@ -43,6 +43,8 @@ public class UserDAOTest extends OlatTestCase { @Autowired private UserManager userManager; + @Autowired + private UserDeletionManager userDeletionManager; @Autowired private UserDAO sut; @@ -177,7 +179,7 @@ public class UserDAOTest extends OlatTestCase { User userWithoutEmailDeleted= identityWithoutEmailDeleted.getUser(); userWithoutEmailDeleted.setProperty(UserConstants.EMAIL, null); userManager.updateUser(userWithoutEmailDeleted); - UserDeletionManager.getInstance().deleteIdentity(identityWithoutEmailDeleted); + userDeletionManager.deleteIdentity(identityWithoutEmailDeleted, null); Identity identityWithoutEmail2 = JunitTestHelper.createAndPersistIdentityAsRndUser("userdao"); User userWithoutEmail2 = identityWithoutEmail2.getUser(); @@ -211,7 +213,7 @@ public class UserDAOTest extends OlatTestCase { User userEmailDuplicateDeleted = identityEmailDuplicateDeleted.getUser(); userEmailDuplicateDeleted.setProperty(UserConstants.EMAIL, emailDuplicate); userManager.updateUser(userEmailDuplicateDeleted); - UserDeletionManager.getInstance().deleteIdentity(identityEmailDuplicateDeleted); + userDeletionManager.deleteIdentity(identityEmailDuplicateDeleted, null); Identity identityEmailDuplicate3 = JunitTestHelper.createAndPersistIdentityAsRndUser("userdao"); User userEmailDuplicate3 = identityEmailDuplicate3.getUser(); diff --git a/src/test/java/org/olat/user/UserTest.java b/src/test/java/org/olat/user/UserTest.java index 908826fa75c..c751883c6b6 100644 --- a/src/test/java/org/olat/user/UserTest.java +++ b/src/test/java/org/olat/user/UserTest.java @@ -355,7 +355,7 @@ public class UserTest extends OlatTestCase { result = securityManager.getIdentitiesByPowerSearch(null, searchValue, false, null, null, null, null, null, null, null, null); assertEquals(1, result.size()); // delete user now - UserDeletionManager.getInstance().deleteIdentity(identity); + UserDeletionManager.getInstance().deleteIdentity(identity, null); dbInstance.commitAndCloseSession(); // check if deleted successfully -- GitLab