View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.fit.console;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.fail;
25  
26  import javax.ws.rs.core.GenericType;
27  import javax.ws.rs.core.Response;
28  import org.apache.commons.lang3.RandomStringUtils;
29  import org.apache.syncope.client.console.SyncopeConsoleSession;
30  import org.apache.syncope.client.console.panels.search.SearchClause;
31  import org.apache.syncope.client.ui.commons.Constants;
32  import org.apache.syncope.common.lib.Attr;
33  import org.apache.syncope.common.lib.SyncopeClientException;
34  import org.apache.syncope.common.lib.request.UserCR;
35  import org.apache.syncope.common.lib.to.LinkedAccountTO;
36  import org.apache.syncope.common.lib.to.ProvisioningResult;
37  import org.apache.syncope.common.lib.to.UserTO;
38  import org.apache.syncope.common.lib.types.ClientExceptionType;
39  import org.apache.syncope.common.rest.api.service.UserService;
40  import org.apache.syncope.fit.core.UserITCase;
41  import org.apache.wicket.Component;
42  import org.apache.wicket.markup.html.form.DropDownChoice;
43  import org.apache.wicket.util.tester.FormTester;
44  import org.junit.jupiter.api.AfterEach;
45  import org.junit.jupiter.api.BeforeEach;
46  import org.junit.jupiter.api.Test;
47  
48  public class LinkedAccountsITCase extends AbstractConsoleITCase {
49  
50      private static final String TAB_PANEL = "body:content:body:container:content:tabbedPanel:panel:searchResult:";
51  
52      private static final String RESULT_DATA_TABLE =
53              "searchResult:container:content:searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable:";
54  
55      private static final String RESOURCES_DATA_TABLE =
56              "view:resources:container:content:searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable:";
57  
58      private static final String SELECT_USER_ACTION = "searchResult:outerObjectsRepeater:1:outer:container:content:"
59              + "togglePanelContainer:container:actions:actions:actionRepeater:0:action:action";
60  
61      private static final String SELECT_RESOURCE_ACTION =
62              "view:resources:outerObjectsRepeater:1:outer:container:content:"
63              + "togglePanelContainer:container:actions:actions:actionRepeater:0:action:action";
64  
65      private static final String PARENT_FORM = "outerObjectsRepeater:2:outer:form:";
66  
67      private static final String FORM = PARENT_FORM + "content:form:";
68  
69      private static final String SEARCH_PANEL = FORM + "view:ownerContainer:search:";
70  
71      private static final String USER_SEARCH_PANEL = SEARCH_PANEL + "usersearch:";
72  
73      private static final String USER_SEARCH_FORM = TAB_PANEL + USER_SEARCH_PANEL
74              + "searchFormContainer:search:multiValueContainer:innerForm:";
75  
76      private static final String CONTAINER = TAB_PANEL + "container:content:";
77  
78      private static UserTO USER;
79  
80      @BeforeEach
81      public void login() {
82          doLogin(ADMIN_UNAME, ADMIN_PWD);
83  
84          // create user with linked account
85          String email = "linkedAccount" + RandomStringUtils.randomNumeric(4) + "@syncope.apache.org";
86          UserCR userCR = UserITCase.getSample(email);
87          String connObjectKeyValue = "uid=" + userCR.getUsername() + ",ou=People,o=isp";
88  
89          LinkedAccountTO account = new LinkedAccountTO.Builder("resource-ldap", connObjectKeyValue).build();
90          account.getPlainAttrs().add(new Attr.Builder("surname").value("LINKED_SURNAME").build());
91          userCR.getLinkedAccounts().add(account);
92  
93          UserService userService = SyncopeConsoleSession.get().getService(UserService.class);
94          Response response = userService.create(userCR);
95          USER = response.readEntity(new GenericType<ProvisioningResult<UserTO>>() {
96          }).getEntity();
97          assertNotNull(USER.getKey());
98          assertEquals(account.getConnObjectKeyValue(), USER.getLinkedAccounts().get(0).getConnObjectKeyValue());
99      }
100 
101     @AfterEach
102     public void cleanUp() {
103         try {
104             SyncopeConsoleSession.get().getService(UserService.class).delete(USER.getKey());
105         } catch (SyncopeClientException e) {
106             if (e.getType() != ClientExceptionType.NotFound) {
107                 throw e;
108             }
109         }
110     }
111 
112     @Test
113     public void createLinkedAccountAndMergeWithUser() {
114         // Locate and select first user
115         TESTER.clickLink("body:realmsLI:realms", false);
116         TESTER.clickLink("body:content:body:container:content:tabbedPanel:tabs-container:tabs:1:link");
117 
118         Component verdiUserComponent = findComponentByProp("username", CONTAINER
119                 + ":searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", "verdi");
120         assertNotNull(verdiUserComponent);
121         TESTER.executeAjaxEvent(verdiUserComponent.getPageRelativePath(), Constants.ON_CLICK);
122 
123         // Click action menu to bring up merge window
124         TESTER.clickLink(TAB_PANEL + "outerObjectsRepeater:1:outer:container:content:togglePanelContainer:container:"
125                 + "actions:actions:actionRepeater:6:action:action");
126         // Search for user
127         TESTER.executeAjaxEvent(USER_SEARCH_FORM + "content:panelPlus:add", Constants.ON_CLICK);
128         FormTester formTester = TESTER.newFormTester(USER_SEARCH_FORM);
129 
130         DropDownChoice<?> type = (DropDownChoice<?>) TESTER.getComponentFromLastRenderedPage(USER_SEARCH_FORM
131                 + "content:view:0:panel:container:type:dropDownChoiceField");
132         TESTER.executeAjaxEvent(USER_SEARCH_FORM + "content:view:0:panel:container:type:dropDownChoiceField",
133                 Constants.ON_CHANGE);
134         type.setModelValue(new String[] { "ATTRIBUTE" });
135         type.setDefaultModelObject(SearchClause.Type.ATTRIBUTE);
136 
137         formTester.setValue("content:view:0:panel:container:property:textField", "username");
138         TESTER.executeAjaxEvent(formTester.getForm().
139                 get("content:view:0:panel:container:property:textField"), Constants.ON_KEYDOWN);
140         formTester.setValue("content:view:0:panel:container:value:textField", USER.getUsername());
141         TESTER.executeAjaxEvent(formTester.getForm().
142                 get("content:view:0:panel:container:value:textField"), Constants.ON_KEYDOWN);
143 
144         TESTER.cleanupFeedbackMessages();
145         Component searchButton = formTester.getForm().
146                 get("content:view:0:panel:container:operatorContainer:operator:search");
147         TESTER.clickLink(searchButton);
148         TESTER.executeAjaxEvent(searchButton.getPageRelativePath(), Constants.ON_CLICK);
149         TESTER.assertNoErrorMessage();
150 
151         // Locate result in data table
152         Component comp = findComponentByProp("username", TAB_PANEL + SEARCH_PANEL + RESULT_DATA_TABLE, USER.
153                 getUsername());
154         TESTER.executeAjaxEvent(comp.getPageRelativePath(), Constants.ON_CLICK);
155 
156         // Select user
157         TESTER.clickLink(TAB_PANEL + SEARCH_PANEL + SELECT_USER_ACTION);
158 
159         // move onto the next panel
160         TESTER.getComponentFromLastRenderedPage(TAB_PANEL + FORM + "view").setEnabled(false);
161         formTester = TESTER.newFormTester(TAB_PANEL + FORM);
162         formTester.submit("buttons:next");
163 
164         // Select a resource
165         comp = findComponentByProp("key", TAB_PANEL + FORM + RESOURCES_DATA_TABLE + "body:rows", "resource-ldap");
166         assertNotNull(comp);
167         TESTER.executeAjaxEvent(comp.getPageRelativePath(), Constants.ON_CLICK);
168         TESTER.clickLink(TAB_PANEL + FORM + SELECT_RESOURCE_ACTION);
169 
170         // move onto the next panel
171         TESTER.getComponentFromLastRenderedPage(TAB_PANEL + FORM + "view").setEnabled(false);
172         formTester = TESTER.newFormTester(TAB_PANEL + FORM);
173         formTester.submit("buttons:next");
174 
175         // Finish merge
176         TESTER.getComponentFromLastRenderedPage(TAB_PANEL + FORM + "view").setEnabled(false);
177         formTester = TESTER.newFormTester(TAB_PANEL + FORM);
178         formTester.submit("buttons:finish");
179 
180         UserService userService = SyncopeConsoleSession.get().getService(UserService.class);
181 
182         // User must have been deleted after the merge
183         try {
184             userService.read(USER.getKey());
185             fail("User must have been deleted; expect an exception here");
186         } catch (SyncopeClientException e) {
187             assertEquals(ClientExceptionType.NotFound, e.getType());
188         }
189         // User must include merged accounts now
190         UserTO verdi = userService.read("verdi");
191         assertFalse(verdi.getLinkedAccounts().isEmpty());
192     }
193 }