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.core;
20  
21  import static org.awaitility.Awaitility.await;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.junit.jupiter.api.Assertions.fail;
27  
28  import java.nio.charset.StandardCharsets;
29  import java.util.concurrent.TimeUnit;
30  import javax.ws.rs.core.Response;
31  import org.apache.commons.io.IOUtils;
32  import org.apache.syncope.common.lib.SyncopeClientException;
33  import org.apache.syncope.common.lib.command.CommandTO;
34  import org.apache.syncope.common.lib.form.FormProperty;
35  import org.apache.syncope.common.lib.form.FormPropertyType;
36  import org.apache.syncope.common.lib.form.SyncopeForm;
37  import org.apache.syncope.common.lib.request.UserCR;
38  import org.apache.syncope.common.lib.to.AnyObjectTO;
39  import org.apache.syncope.common.lib.to.ExecTO;
40  import org.apache.syncope.common.lib.to.FormPropertyDefTO;
41  import org.apache.syncope.common.lib.to.ImplementationTO;
42  import org.apache.syncope.common.lib.to.MacroTaskTO;
43  import org.apache.syncope.common.lib.to.RoleTO;
44  import org.apache.syncope.common.lib.to.UserTO;
45  import org.apache.syncope.common.lib.types.ClientExceptionType;
46  import org.apache.syncope.common.lib.types.ExecStatus;
47  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
48  import org.apache.syncope.common.lib.types.IdRepoImplementationType;
49  import org.apache.syncope.common.lib.types.ImplementationEngine;
50  import org.apache.syncope.common.lib.types.TaskType;
51  import org.apache.syncope.common.rest.api.RESTHeaders;
52  import org.apache.syncope.common.rest.api.beans.ExecSpecs;
53  import org.apache.syncope.common.rest.api.beans.RealmQuery;
54  import org.apache.syncope.common.rest.api.beans.TaskQuery;
55  import org.apache.syncope.common.rest.api.service.TaskService;
56  import org.apache.syncope.fit.AbstractITCase;
57  import org.apache.syncope.fit.core.reference.TestCommand;
58  import org.apache.syncope.fit.core.reference.TestCommandArgs;
59  import org.apache.syncope.fit.core.reference.TestMacroActions;
60  import org.junit.jupiter.api.AfterAll;
61  import org.junit.jupiter.api.BeforeAll;
62  import org.junit.jupiter.api.Test;
63  
64  public class MacroTaskITCase extends AbstractITCase {
65  
66      private static String MACRO_TASK_KEY;
67  
68      private static final TestCommandArgs TCA = new TestCommandArgs();
69  
70      static {
71          TCA.setParentRealm("${parent}");
72          TCA.setRealmName("${realm}");
73          TCA.setPrinterName("aprinter112");
74      }
75  
76      @BeforeAll
77      public static void testCommandsSetup() throws Exception {
78          CommandITCase.testCommandSetup();
79  
80          ImplementationTO transformer = null;
81          try {
82              transformer = IMPLEMENTATION_SERVICE.read(
83                      IdRepoImplementationType.COMMAND, "GroovyCommand");
84          } catch (SyncopeClientException e) {
85              if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
86                  transformer = new ImplementationTO();
87                  transformer.setKey("GroovyCommand");
88                  transformer.setEngine(ImplementationEngine.GROOVY);
89                  transformer.setType(IdRepoImplementationType.COMMAND);
90                  transformer.setBody(IOUtils.toString(MacroTaskITCase.class.getResourceAsStream("/GroovyCommand.groovy"),
91                          StandardCharsets.UTF_8));
92                  Response response = IMPLEMENTATION_SERVICE.create(transformer);
93                  transformer = IMPLEMENTATION_SERVICE.read(
94                          transformer.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
95                  assertNotNull(transformer.getKey());
96              }
97          }
98          assertNotNull(transformer);
99  
100         ImplementationTO macroActions = null;
101         try {
102             macroActions = IMPLEMENTATION_SERVICE.read(IdRepoImplementationType.MACRO_ACTIONS,
103                     TestMacroActions.class.getSimpleName());
104         } catch (SyncopeClientException e) {
105             if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
106                 macroActions = new ImplementationTO();
107                 macroActions.setKey(TestMacroActions.class.getSimpleName());
108                 macroActions.setEngine(ImplementationEngine.JAVA);
109                 macroActions.setType(IdRepoImplementationType.MACRO_ACTIONS);
110                 macroActions.setBody(TestMacroActions.class.getName());
111                 Response response = IMPLEMENTATION_SERVICE.create(macroActions);
112                 macroActions = IMPLEMENTATION_SERVICE.read(
113                         macroActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
114                 assertNotNull(macroActions.getKey());
115             }
116         }
117         assertNotNull(macroActions);
118 
119         if (MACRO_TASK_KEY == null) {
120             MACRO_TASK_KEY = TASK_SERVICE.<MacroTaskTO>search(
121                     new TaskQuery.Builder(TaskType.MACRO).build()).getResult().
122                     stream().filter(t -> "Test Macro".equals(t.getName())).findFirst().map(MacroTaskTO::getKey).
123                     orElseGet(() -> {
124                         MacroTaskTO task = new MacroTaskTO();
125                         task.setName("Test Macro");
126                         task.setActive(true);
127                         task.setRealm("/odd");
128                         task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
129                         task.getCommands().add(
130                                 new CommandTO.Builder(TestCommand.class.getSimpleName()).args(TCA).build());
131 
132                         FormPropertyDefTO realm = new FormPropertyDefTO();
133                         realm.setKey("realm");
134                         realm.setName("Realm");
135                         realm.setWritable(true);
136                         realm.setRequired(true);
137                         realm.setType(FormPropertyType.String);
138                         task.getFormPropertyDefs().add(realm);
139 
140                         FormPropertyDefTO parent = new FormPropertyDefTO();
141                         parent.setKey("parent");
142                         parent.setName("Parent Realm");
143                         parent.setWritable(true);
144                         parent.setRequired(true);
145                         parent.setType(FormPropertyType.Dropdown);
146                         task.getFormPropertyDefs().add(parent);
147 
148                         task.setMacroActions(TestMacroActions.class.getSimpleName());
149 
150                         Response response = TASK_SERVICE.create(TaskType.MACRO, task);
151                         return response.getHeaderString(RESTHeaders.RESOURCE_KEY);
152                     });
153         }
154     }
155 
156     @AfterAll
157     public static void cleanup() {
158         TestCommandArgs args = new TestCommandArgs();
159         try {
160             ANY_OBJECT_SERVICE.delete(args.getPrinterName());
161             REALM_SERVICE.delete(args.getParentRealm() + "/" + args.getRealmName());
162         } catch (Exception e) {
163             // ignore
164         }
165     }
166 
167     @Test
168     public void execute() {
169         SyncopeForm form = TASK_SERVICE.getMacroTaskForm(MACRO_TASK_KEY);
170         form.getProperty("realm").orElseThrow().setValue("macro");
171         FormProperty parent = form.getProperty("parent").orElseThrow();
172         assertTrue(parent.getDropdownValues().stream().anyMatch(v -> "/odd".equals(v.getKey())));
173         parent.setValue("/odd");
174 
175         int preExecs = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
176         ExecTO execution = TASK_SERVICE.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build(), form);
177         assertNotNull(execution.getExecutor());
178 
179         await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {
180             try {
181                 return preExecs < TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
182             } catch (Exception e) {
183                 return false;
184             }
185         });
186 
187         ExecTO exec = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().get(preExecs);
188         assertEquals(ExecStatus.SUCCESS.name(), exec.getStatus());
189 
190         AnyObjectTO printer = ANY_OBJECT_SERVICE.read(PRINTER, TCA.getPrinterName());
191         assertNotNull(printer);
192         assertEquals("/odd/macro", printer.getRealm());
193         assertFalse(REALM_SERVICE.search(
194                 new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty());
195     }
196 
197     @Test
198     public void saveSameCommandMultipleOccurrencies() {
199         TestCommandArgs tca1 = new TestCommandArgs();
200         tca1.setParentRealm("parent1");
201         tca1.setRealmName("realm1");
202         tca1.setPrinterName("printer1");
203 
204         MacroTaskTO task = new MacroTaskTO();
205         task.setName("saveSameCommandMultipleOccurrencies");
206         task.setActive(true);
207         task.setRealm("/");
208         task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
209         task.getCommands().add(new CommandTO.Builder(TestCommand.class.getSimpleName()).args(tca1).build());
210         task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
211 
212         Response response = TASK_SERVICE.create(TaskType.MACRO, task);
213         String newTaskKey = response.getHeaderString(RESTHeaders.RESOURCE_KEY);
214 
215         task = TASK_SERVICE.<MacroTaskTO>read(TaskType.MACRO, newTaskKey, false);
216         assertEquals(3, task.getCommands().size());
217         assertEquals("GroovyCommand", task.getCommands().get(0).getKey());
218         assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(1).getKey());
219         assertEquals(tca1, task.getCommands().get(1).getArgs());
220         assertEquals("GroovyCommand", task.getCommands().get(2).getKey());
221 
222         TestCommandArgs tca2 = new TestCommandArgs();
223         tca2.setParentRealm("parent2");
224         tca2.setRealmName("realm2");
225         tca2.setPrinterName("printer2");
226         task.getCommands().add(new CommandTO.Builder(TestCommand.class.getSimpleName()).args(tca2).build());
227 
228         TASK_SERVICE.update(TaskType.MACRO, task);
229 
230         task = TASK_SERVICE.<MacroTaskTO>read(TaskType.MACRO, newTaskKey, false);
231         assertEquals(4, task.getCommands().size());
232         assertEquals("GroovyCommand", task.getCommands().get(0).getKey());
233         assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(1).getKey());
234         assertEquals(tca1, task.getCommands().get(1).getArgs());
235         assertEquals("GroovyCommand", task.getCommands().get(2).getKey());
236         assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(3).getKey());
237         assertEquals(tca2, task.getCommands().get(3).getArgs());
238     }
239 
240     @Test
241     public void cantExecute() {
242         // 1. create Role for task execution
243         RoleTO role = new RoleTO();
244         role.setKey("new" + getUUIDString());
245         role.getRealms().add("/even");
246         role.getEntitlements().add(IdRepoEntitlement.TASK_EXECUTE);
247         role = createRole(role);
248         assertNotNull(role);
249 
250         // 2. create User with such a Role granted
251         UserCR userCR = UserITCase.getUniqueSample("cantrunncommand@test.org");
252         userCR.getRoles().add(role.getKey());
253         UserTO userTO = createUser(userCR).getEntity();
254         assertNotNull(userTO);
255 
256         // 3. attempt to run the macro task -> fail
257         TaskService taskService = CLIENT_FACTORY.create(
258                 userTO.getUsername(), "password123").getService(TaskService.class);
259         try {
260             taskService.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build());
261             fail();
262         } catch (SyncopeClientException e) {
263             assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
264         }
265     }
266 }