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.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.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.io.IOException;
28  import java.security.AccessControlException;
29  import java.util.List;
30  import javax.ws.rs.ForbiddenException;
31  import javax.ws.rs.core.EntityTag;
32  import javax.ws.rs.core.GenericType;
33  import javax.ws.rs.core.HttpHeaders;
34  import javax.ws.rs.core.MediaType;
35  import javax.ws.rs.core.MultivaluedMap;
36  import javax.ws.rs.core.Response;
37  import org.apache.commons.lang3.RandomStringUtils;
38  import org.apache.commons.lang3.StringUtils;
39  import org.apache.cxf.jaxrs.client.WebClient;
40  import org.apache.syncope.client.lib.BasicAuthenticationHandler;
41  import org.apache.syncope.client.lib.SyncopeClient;
42  import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
43  import org.apache.syncope.common.lib.SyncopeClientException;
44  import org.apache.syncope.common.lib.request.GroupCR;
45  import org.apache.syncope.common.lib.request.GroupUR;
46  import org.apache.syncope.common.lib.request.StringReplacePatchItem;
47  import org.apache.syncope.common.lib.request.UserUR;
48  import org.apache.syncope.common.lib.to.ConnInstanceTO;
49  import org.apache.syncope.common.lib.to.GroupTO;
50  import org.apache.syncope.common.lib.to.ProvisioningResult;
51  import org.apache.syncope.common.lib.to.UserTO;
52  import org.apache.syncope.common.lib.types.ClientExceptionType;
53  import org.apache.syncope.common.rest.api.Preference;
54  import org.apache.syncope.common.rest.api.RESTHeaders;
55  import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
56  import org.apache.syncope.common.rest.api.service.ConnectorService;
57  import org.apache.syncope.common.rest.api.service.GroupService;
58  import org.apache.syncope.common.rest.api.service.UserService;
59  import org.apache.syncope.fit.AbstractITCase;
60  import org.junit.jupiter.api.Test;
61  
62  public class RESTITCase extends AbstractITCase {
63  
64      @Test
65      public void unauthorizedOrForbidden() {
66          // service as admin: it works
67          List<ConnInstanceTO> connectors = CONNECTOR_SERVICE.list(null);
68          assertNotNull(connectors);
69          assertFalse(connectors.isEmpty());
70  
71          // service with bad password: 401 unauthorized
72          try {
73              CLIENT_FACTORY.create("bellini", "passwor");
74              fail("This should not happen");
75          } catch (AccessControlException e) {
76              assertNotNull(e);
77          }
78  
79          // service with invalid JWT string: 401 unauthorized
80          try {
81              CLIENT_FACTORY.create(RandomStringUtils.random(20, true, true)).self();
82              fail("This should not happen");
83          } catch (AccessControlException e) {
84              assertNotNull(e);
85          }
86  
87          // service with good password, but no entitlements owned: 403 forbidden
88          SyncopeClient goodClient = CLIENT_FACTORY.create("bellini", "password");
89          try {
90              goodClient.getService(ConnectorService.class).list(null);
91              fail("This should not happen");
92          } catch (ForbiddenException e) {
93              assertNotNull(e);
94          }
95      }
96  
97      @Test
98      public void noContent() throws IOException {
99          SyncopeClient noContentclient = CLIENT_FACTORY.create(ADMIN_UNAME, ADMIN_PWD);
100         GroupService noContentService = SyncopeClient.prefer(
101                 noContentclient.getService(GroupService.class), Preference.RETURN_NO_CONTENT);
102 
103         GroupCR groupCR = GroupITCase.getSample("noContent");
104 
105         Response response = noContentService.create(groupCR);
106         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
107         assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
108         assertEquals(StringUtils.EMPTY, response.readEntity(String.class));
109 
110         GroupTO group = getObject(response.getLocation(), GroupService.class, GroupTO.class);
111         assertNotNull(group);
112 
113         GroupUR groupUR = new GroupUR();
114         groupUR.setKey(group.getKey());
115         groupUR.getPlainAttrs().add(attrAddReplacePatch("badge", "xxxxxxxxxx"));
116 
117         response = noContentService.update(groupUR);
118         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
119         assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
120         assertEquals(StringUtils.EMPTY, response.readEntity(String.class));
121 
122         response = noContentService.delete(group.getKey());
123         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
124         assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
125         assertEquals(StringUtils.EMPTY, response.readEntity(String.class));
126     }
127 
128     @Test
129     public void ifMatch() {
130         UserTO userTO = USER_SERVICE.create(UserITCase.getUniqueSample("ifmatch@syncope.apache.org")).
131                 readEntity(new GenericType<ProvisioningResult<UserTO>>() {
132                 }).getEntity();
133         assertNotNull(userTO);
134         assertNotNull(userTO.getKey());
135 
136         EntityTag etag = SyncopeClient.getLatestEntityTag(USER_SERVICE);
137         assertNotNull(etag);
138         assertTrue(StringUtils.isNotBlank(etag.getValue()));
139 
140         UserUR userUR = new UserUR();
141         userUR.setKey(userTO.getKey());
142         userUR.setUsername(new StringReplacePatchItem.Builder().value(userTO.getUsername() + "XX").build());
143         userTO = USER_SERVICE.update(userUR).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
144         }).getEntity();
145         assertTrue(userTO.getUsername().endsWith("XX"));
146         EntityTag etag1 = SyncopeClient.getLatestEntityTag(USER_SERVICE);
147         assertFalse(etag.getValue().equals(etag1.getValue()));
148 
149         UserService ifMatchService = SyncopeClient.ifMatch(ADMIN_CLIENT.getService(UserService.class), etag);
150         userUR.setUsername(new StringReplacePatchItem.Builder().value(userTO.getUsername() + "YY").build());
151         try {
152             ifMatchService.update(userUR);
153             fail("This should not happen");
154         } catch (SyncopeClientException e) {
155             assertEquals(ClientExceptionType.ConcurrentModification, e.getType());
156         }
157 
158         userTO = USER_SERVICE.read(userTO.getKey());
159         assertTrue(userTO.getUsername().endsWith("XX"));
160     }
161 
162     @Test
163     public void defaultContentType() {
164         // manualy instantiate SyncopeClient so that media type can be set to */*
165         SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean().setAddress(ADDRESS);
166         SyncopeClient client = new SyncopeClient(
167                 MediaType.WILDCARD_TYPE,
168                 factory.getRestClientFactoryBean(),
169                 factory.getExceptionMapper(),
170                 new BasicAuthenticationHandler(ADMIN_UNAME, ADMIN_PWD),
171                 false,
172                 null,
173                 null);
174 
175         // perform operation
176         AnyTypeClassService service = client.getService(AnyTypeClassService.class);
177         service.list();
178 
179         // check that */* was actually sent
180         MultivaluedMap<String, String> requestHeaders = WebClient.client(service).getHeaders();
181         assertEquals(MediaType.WILDCARD, requestHeaders.getFirst(HttpHeaders.ACCEPT));
182 
183         // check that application/json was received
184         String contentType = WebClient.client(service).getResponse().getHeaderString(HttpHeaders.CONTENT_TYPE);
185         assertTrue(contentType.startsWith(MediaType.APPLICATION_JSON));
186     }
187 
188     @Test
189     public void exportInternalStorageContent() throws IOException {
190         Response response = SYNCOPE_SERVICE.exportInternalStorageContent(100);
191         assertNotNull(response);
192         assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
193         assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_XML));
194         String contentDisposition = response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
195         assertNotNull(contentDisposition);
196 
197         String configExport = response.readEntity(String.class);
198         assertFalse(configExport.isEmpty());
199         assertTrue(configExport.length() > 1000);
200     }
201 }