Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* initial pass at merging users/accounts * clean up config; working on wizard and user selection * clean up config; working on wizard and user selection * polish * working on account review wizard page * working on batch items * working on batch requests * testing batch ops * fix tests * fix test * fix formatting issues * apply changes based on review suggestions * working on ITs for merge-accounts feature * updated test * adjustments to query filter * tweaks to UI * cntd with wicket changes and merging functionality * working on merge wizard. added panels to show resources, and preview * add panel for preview * polish * polish * cntd with tests - wip * addressing feedback after review - tests WIP. * fix issues after review * highlight item when selected in data table * work out events with item selection after the merge * fix tests - let ci run * fix checkstyle
- Loading branch information
Showing
45 changed files
with
1,472 additions
and
8 deletions.
There are no files selected for viewing
182 changes: 182 additions & 0 deletions
182
...main/java/org/apache/syncope/client/console/panels/MergeLinkedAccountsResourcesPanel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.apache.syncope.client.console.panels; | ||
|
||
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; | ||
import org.apache.syncope.client.console.SyncopeConsoleApplication; | ||
import org.apache.syncope.client.console.commons.Constants; | ||
import org.apache.syncope.client.console.commons.DirectoryDataProvider; | ||
import org.apache.syncope.client.console.commons.SortableDataProviderComparator; | ||
import org.apache.syncope.client.console.pages.BasePage; | ||
import org.apache.syncope.client.console.rest.ResourceRestClient; | ||
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; | ||
import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; | ||
import org.apache.syncope.client.console.wizards.any.MergeLinkedAccountsWizardModel; | ||
import org.apache.syncope.common.lib.to.ResourceTO; | ||
import org.apache.syncope.common.lib.types.StandardEntitlement; | ||
import org.apache.wicket.PageReference; | ||
import org.apache.wicket.ajax.AjaxRequestTarget; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; | ||
import org.apache.wicket.extensions.wizard.WizardModel.ICondition; | ||
import org.apache.wicket.extensions.wizard.WizardStep; | ||
import org.apache.wicket.model.CompoundPropertyModel; | ||
import org.apache.wicket.model.IModel; | ||
import org.apache.wicket.model.Model; | ||
import org.apache.wicket.model.ResourceModel; | ||
import org.apache.wicket.model.StringResourceModel; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
|
||
public class MergeLinkedAccountsResourcesPanel extends WizardStep implements ICondition { | ||
private static final long serialVersionUID = 1221037007528732347L; | ||
|
||
private final MergeLinkedAccountsWizardModel wizardModel; | ||
|
||
public MergeLinkedAccountsResourcesPanel(final MergeLinkedAccountsWizardModel wizardModel, | ||
final PageReference pageReference) { | ||
super(); | ||
setOutputMarkupId(true); | ||
this.wizardModel = wizardModel; | ||
add(new ResourceSelectionDirectoryPanel("resources", pageReference)); | ||
} | ||
|
||
@Override | ||
public boolean evaluate() { | ||
return SyncopeConsoleApplication.get().getSecuritySettings().getAuthorizationStrategy(). | ||
isActionAuthorized(this, RENDER); | ||
} | ||
|
||
@Override | ||
public String getTitle() { | ||
setSummaryModel(new StringResourceModel("mergeLinkedAccounts.searchResource.summary", | ||
Model.of(wizardModel.getMergingUser()))); | ||
setTitleModel(new StringResourceModel("mergeLinkedAccounts.searchResource.title", | ||
Model.of(wizardModel.getMergingUser()))); | ||
return super.getTitle(); | ||
} | ||
|
||
private class ResourceSelectionDirectoryPanel extends | ||
DirectoryPanel<ResourceTO, ResourceTO, | ||
ResourceSelectionDirectoryPanel.ResourcesDataProvider, ResourceRestClient> { | ||
|
||
private static final long serialVersionUID = 6005711052393825472L; | ||
|
||
ResourceSelectionDirectoryPanel(final String id, final PageReference pageReference) { | ||
super(id, pageReference, true); | ||
|
||
this.restClient = new ResourceRestClient(); | ||
modal.size(Modal.Size.Large); | ||
setOutputMarkupId(true); | ||
disableCheckBoxes(); | ||
initResultTable(); | ||
} | ||
|
||
@Override | ||
protected ResourcesDataProvider dataProvider() { | ||
return new ResourcesDataProvider(this.rows); | ||
} | ||
|
||
@Override | ||
protected String paginatorRowsKey() { | ||
return Constants.PREF_RESOURCES_PAGINATOR_ROWS; | ||
} | ||
|
||
@Override | ||
protected List<IColumn<ResourceTO, String>> getColumns() { | ||
List<IColumn<ResourceTO, String>> columns = new ArrayList<>(); | ||
columns.add(new PropertyColumn<>(new ResourceModel("resource"), "key", "key")); | ||
return columns; | ||
} | ||
|
||
@Override | ||
protected ActionsPanel<ResourceTO> getActions(final IModel<ResourceTO> model) { | ||
final ActionsPanel<ResourceTO> panel = super.getActions(model); | ||
panel.add(new ActionLink<ResourceTO>() { | ||
private static final long serialVersionUID = -7978723352517770644L; | ||
|
||
@Override | ||
public void onClick(final AjaxRequestTarget target, final ResourceTO resource) { | ||
MergeLinkedAccountsWizardModel model = MergeLinkedAccountsResourcesPanel.this.wizardModel; | ||
String connObjectKeyValue = restClient.getConnObjectKeyValue( | ||
resource.getKey(), | ||
model.getMergingUser().getType(), | ||
model.getMergingUser().getKey()); | ||
if (connObjectKeyValue != null) { | ||
model.setResource(resource); | ||
String tableId = MergeLinkedAccountsResourcesPanel.this. | ||
get("resources:container:content:searchContainer:resultTable" | ||
+ ":tablePanel:groupForm:checkgroup:dataTable"). | ||
getMarkupId(); | ||
String js = "$('#" + tableId + "').removeClass('active');"; | ||
js += "$('#" + tableId + " tbody tr td div').filter(function() " | ||
+ "{return $(this).text() === \"" + resource.getKey() + "\";})" | ||
+ ".parent().parent().addClass('active');"; | ||
target.prependJavaScript(js); | ||
|
||
} else { | ||
error("Unable to determine connector object key"); | ||
((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); | ||
} | ||
} | ||
}, ActionLink.ActionType.SELECT, StandardEntitlement.RESOURCE_READ); | ||
return panel; | ||
} | ||
|
||
@Override | ||
protected Collection<ActionLink.ActionType> getBatches() { | ||
return Collections.emptyList(); | ||
} | ||
|
||
protected final class ResourcesDataProvider extends DirectoryDataProvider<ResourceTO> { | ||
|
||
private static final long serialVersionUID = -185944053385660794L; | ||
|
||
private final SortableDataProviderComparator<ResourceTO> comparator; | ||
|
||
private ResourcesDataProvider(final int paginatorRows) { | ||
super(paginatorRows); | ||
setSort("key", SortOrder.ASCENDING); | ||
comparator = new SortableDataProviderComparator<>(this); | ||
} | ||
|
||
@Override | ||
public Iterator<ResourceTO> iterator(final long first, final long count) { | ||
List<ResourceTO> list = restClient.list(); | ||
Collections.sort(list, comparator); | ||
return list.subList((int) first, (int) first + (int) count).iterator(); | ||
} | ||
|
||
@Override | ||
public long size() { | ||
return restClient.list().size(); | ||
} | ||
|
||
@Override | ||
public IModel<ResourceTO> model(final ResourceTO object) { | ||
return new CompoundPropertyModel<>(object); | ||
} | ||
} | ||
} | ||
} |
177 changes: 177 additions & 0 deletions
177
...rc/main/java/org/apache/syncope/client/console/panels/MergeLinkedAccountsReviewPanel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.apache.syncope.client.console.panels; | ||
|
||
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; | ||
import org.apache.syncope.client.console.commons.DirectoryDataProvider; | ||
import org.apache.syncope.client.console.commons.SortableDataProviderComparator; | ||
import org.apache.syncope.client.console.rest.ResourceRestClient; | ||
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn; | ||
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; | ||
import org.apache.syncope.client.console.wizards.any.MergeLinkedAccountsWizardModel; | ||
import org.apache.syncope.common.lib.to.LinkedAccountTO; | ||
import org.apache.syncope.common.lib.to.UserTO; | ||
import org.apache.wicket.PageReference; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; | ||
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; | ||
import org.apache.wicket.extensions.wizard.WizardStep; | ||
import org.apache.wicket.model.CompoundPropertyModel; | ||
import org.apache.wicket.model.IModel; | ||
import org.apache.wicket.model.ResourceModel; | ||
import org.apache.wicket.model.StringResourceModel; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class MergeLinkedAccountsReviewPanel extends WizardStep { | ||
private static final long serialVersionUID = 1221037007528732347L; | ||
|
||
public MergeLinkedAccountsReviewPanel(final MergeLinkedAccountsWizardModel wizardModel, | ||
final PageReference pageReference) { | ||
super(); | ||
setOutputMarkupId(true); | ||
setTitleModel(new StringResourceModel("mergeLinkedAccounts.reviewAccounts.title")); | ||
add(new LinkedAccountsReviewDirectoryPanel("linkedAccounts", pageReference, wizardModel)); | ||
} | ||
|
||
private static class LinkedAccountsReviewDirectoryPanel extends | ||
DirectoryPanel<LinkedAccountTO, LinkedAccountTO, | ||
LinkedAccountsReviewDirectoryPanel.LinkedAccountsDataProvider, ResourceRestClient> { | ||
|
||
private static final String PAGINATOR_ROWS = "linked.account.review.paginator.rows"; | ||
|
||
private static final long serialVersionUID = 6005711052393825472L; | ||
|
||
private final MergeLinkedAccountsWizardModel wizardModel; | ||
|
||
LinkedAccountsReviewDirectoryPanel(final String id, final PageReference pageReference, | ||
final MergeLinkedAccountsWizardModel wizardModel) { | ||
super(id, pageReference, true); | ||
this.restClient = new ResourceRestClient(); | ||
this.wizardModel = wizardModel; | ||
modal.size(Modal.Size.Large); | ||
setOutputMarkupId(true); | ||
disableCheckBoxes(); | ||
initResultTable(); | ||
} | ||
|
||
@Override | ||
protected LinkedAccountsDataProvider dataProvider() { | ||
return new LinkedAccountsDataProvider(this.rows); | ||
} | ||
|
||
@Override | ||
protected String paginatorRowsKey() { | ||
return PAGINATOR_ROWS; | ||
} | ||
|
||
@Override | ||
protected List<IColumn<LinkedAccountTO, String>> getColumns() { | ||
List<IColumn<LinkedAccountTO, String>> columns = new ArrayList<>(); | ||
columns.add(new PropertyColumn<>(new ResourceModel("resource"), "resource", "resource")); | ||
columns.add(new PropertyColumn<>( | ||
new ResourceModel("connObjectKeyValue"), "connObjectKeyValue", "connObjectKeyValue")); | ||
columns.add(new PropertyColumn<>( | ||
new ResourceModel("username"), "username", "username")); | ||
columns.add(new BooleanPropertyColumn<>( | ||
new ResourceModel("suspended"), "suspended", "suspended")); | ||
return columns; | ||
} | ||
|
||
@Override | ||
protected Collection<ActionLink.ActionType> getBatches() { | ||
return Collections.emptyList(); | ||
} | ||
|
||
private List<LinkedAccountTO> previewAccounts() { | ||
UserTO mergingUser = wizardModel.getMergingUser(); | ||
|
||
// Move linked accounts into the target/base user as linked accounts | ||
List<LinkedAccountTO> accounts = mergingUser.getLinkedAccounts().stream().map(acct -> { | ||
LinkedAccountTO linkedAccount = | ||
new LinkedAccountTO.Builder(acct.getResource(), acct.getConnObjectKeyValue()) | ||
.password(acct.getPassword()) | ||
.suspended(acct.isSuspended()) | ||
.username(acct.getUsername()) | ||
.build(); | ||
linkedAccount.getPlainAttrs().addAll(acct.getPlainAttrs()); | ||
linkedAccount.getPrivileges().addAll(acct.getPrivileges()); | ||
return linkedAccount; | ||
}).collect(Collectors.toList()); | ||
|
||
// Move merging user's resources into the target/base user as a linked account | ||
accounts.addAll(mergingUser.getResources().stream().map(resource -> { | ||
String connObjectKeyValue = restClient.getConnObjectKeyValue(resource, | ||
mergingUser.getType(), mergingUser.getKey()); | ||
return new LinkedAccountTO.Builder(resource, connObjectKeyValue).build(); | ||
}).collect(Collectors.toList())); | ||
|
||
// Move merging user into target/base user as a linked account | ||
String connObjectKeyValue = restClient.getConnObjectKeyValue( | ||
wizardModel.getResource().getKey(), | ||
mergingUser.getType(), mergingUser.getKey()); | ||
LinkedAccountTO linkedAccount = | ||
new LinkedAccountTO.Builder(wizardModel.getResource().getKey(), connObjectKeyValue) | ||
.password(mergingUser.getPassword()) | ||
.suspended(mergingUser.isSuspended()) | ||
.username(mergingUser.getUsername()) | ||
.build(); | ||
linkedAccount.getPlainAttrs().addAll(mergingUser.getPlainAttrs()); | ||
linkedAccount.getPrivileges().addAll(mergingUser.getPrivileges()); | ||
accounts.add(linkedAccount); | ||
|
||
return accounts; | ||
} | ||
|
||
protected final class LinkedAccountsDataProvider extends DirectoryDataProvider<LinkedAccountTO> { | ||
|
||
private static final long serialVersionUID = -185944053385660794L; | ||
|
||
private final SortableDataProviderComparator<LinkedAccountTO> comparator; | ||
|
||
private LinkedAccountsDataProvider(final int paginatorRows) { | ||
super(paginatorRows); | ||
setSort("resource", SortOrder.ASCENDING); | ||
comparator = new SortableDataProviderComparator<>(this); | ||
} | ||
|
||
@Override | ||
public Iterator<LinkedAccountTO> iterator(final long first, final long count) { | ||
List<LinkedAccountTO> list = previewAccounts(); | ||
Collections.sort(list, comparator); | ||
return list.subList((int) first, (int) first + (int) count).iterator(); | ||
} | ||
|
||
@Override | ||
public long size() { | ||
return previewAccounts().size(); | ||
} | ||
|
||
@Override | ||
public IModel<LinkedAccountTO> model(final LinkedAccountTO object) { | ||
return new CompoundPropertyModel<>(object); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.