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.common.rest.api.service;
20  
21  import io.swagger.v3.oas.annotations.Operation;
22  import io.swagger.v3.oas.annotations.Parameter;
23  import io.swagger.v3.oas.annotations.enums.ParameterIn;
24  import io.swagger.v3.oas.annotations.headers.Header;
25  import io.swagger.v3.oas.annotations.media.Content;
26  import io.swagger.v3.oas.annotations.media.Schema;
27  import io.swagger.v3.oas.annotations.responses.ApiResponse;
28  import io.swagger.v3.oas.annotations.responses.ApiResponses;
29  import io.swagger.v3.oas.annotations.security.SecurityRequirement;
30  import io.swagger.v3.oas.annotations.tags.Tag;
31  import javax.validation.constraints.NotNull;
32  import javax.ws.rs.Consumes;
33  import javax.ws.rs.DELETE;
34  import javax.ws.rs.GET;
35  import javax.ws.rs.PATCH;
36  import javax.ws.rs.POST;
37  import javax.ws.rs.PUT;
38  import javax.ws.rs.Path;
39  import javax.ws.rs.Produces;
40  import javax.ws.rs.QueryParam;
41  import javax.ws.rs.core.HttpHeaders;
42  import javax.ws.rs.core.MediaType;
43  import javax.ws.rs.core.Response;
44  import org.apache.syncope.common.lib.request.PasswordPatch;
45  import org.apache.syncope.common.lib.request.StatusR;
46  import org.apache.syncope.common.lib.request.UserCR;
47  import org.apache.syncope.common.lib.request.UserUR;
48  import org.apache.syncope.common.lib.to.ProvisioningResult;
49  import org.apache.syncope.common.lib.to.UserTO;
50  import org.apache.syncope.common.rest.api.RESTHeaders;
51  import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
52  
53  /**
54   * REST operations for user self-management.
55   */
56  @Tag(name = "UserSelf")
57  @Path("users/self")
58  public interface UserSelfService extends JAXRSService {
59  
60      /**
61       * Returns the user making the service call.
62       *
63       * @return calling user data, including own UUID, entitlements and delegations
64       */
65      @Operation(security = {
66          @SecurityRequirement(name = "BasicAuthentication"),
67          @SecurityRequirement(name = "Bearer") })
68      @ApiResponses(
69              @ApiResponse(responseCode = "200",
70                      description = "Calling user data, including own UUID, entitlements and delegations", content =
71                      @Content(schema =
72                              @Schema(implementation = UserTO.class)), headers = {
73                  @Header(name = RESTHeaders.RESOURCE_KEY, schema =
74                          @Schema(type = "string"),
75                          description = "UUID of the calling user"),
76                  @Header(name = RESTHeaders.OWNED_ENTITLEMENTS, schema =
77                          @Schema(type = "string"),
78                          description = "List of entitlements owned by the calling user")
79              }))
80      @GET
81      @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
82      Response read();
83  
84      /**
85       * Self-registration for new user.
86       *
87       * @param createReq user to be created
88       * @return Response object featuring Location header of self-registered user as well as the user itself
89       * enriched with propagation status information
90       */
91      @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
92              description = "Allows client to specify a preference for the result to be returned from the server",
93              allowEmptyValue = true, schema =
94              @Schema(defaultValue = "return-content", allowableValues = { "return-content", "return-no-content" }))
95      @ApiResponses(
96              @ApiResponse(responseCode = "201",
97                      description = "User successfully created enriched with propagation status information, as Entity,"
98                      + " or empty if 'Prefer: return-no-content' was specified",
99                      content =
100                     @Content(schema =
101                             @Schema(implementation = ProvisioningResult.class)), headers = {
102                 @Header(name = RESTHeaders.RESOURCE_KEY, schema =
103                         @Schema(type = "string"),
104                         description = "UUID generated for the user created"),
105                 @Header(name = HttpHeaders.LOCATION, schema =
106                         @Schema(type = "string"),
107                         description = "URL of the user created"),
108                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
109                         @Schema(type = "string"),
110                         description = "Allows the server to inform the "
111                         + "client about the fact that a specified preference was applied") }))
112     @POST
113     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
114     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
115     Response create(@NotNull UserCR createReq);
116 
117     /**
118      * Self-updates user.
119      *
120      * @param updateReq modification to be applied to self
121      * @return Response object featuring the updated user
122      */
123     @Operation(security = {
124         @SecurityRequirement(name = "BasicAuthentication"),
125         @SecurityRequirement(name = "Bearer") })
126     @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
127             description = "Allows client to specify a preference for the result to be returned from the server",
128             allowEmptyValue = true, schema =
129             @Schema(defaultValue = "return-content", allowableValues = { "return-content", "return-no-content" }))
130     @Parameter(name = "key", description = "User's key", in = ParameterIn.PATH, schema =
131             @Schema(type = "string"))
132     @ApiResponses({
133         @ApiResponse(responseCode = "200",
134                 description = "User successfully updated enriched with propagation status information, as Entity",
135                 content =
136                 @Content(schema =
137                         @Schema(implementation = ProvisioningResult.class))),
138         @ApiResponse(responseCode = "204",
139                 description = "No content if 'Prefer: return-no-content' was specified", headers =
140                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
141                         @Schema(type = "string"),
142                         description = "Allows the server to inform the "
143                         + "client about the fact that a specified preference was applied")) })
144     @PATCH
145     @Path("{key}")
146     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
147     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
148     Response update(@NotNull UserUR updateReq);
149 
150     /**
151      * Self-updates user.
152      *
153      * @param user complete update
154      * @return Response object featuring the updated user
155      */
156     @Operation(security = {
157         @SecurityRequirement(name = "BasicAuthentication"),
158         @SecurityRequirement(name = "Bearer") })
159     @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
160             description = "Allows client to specify a preference for the result to be returned from the server",
161             allowEmptyValue = true, schema =
162             @Schema(defaultValue = "return-content", allowableValues = { "return-content", "return-no-content" }))
163     @Parameter(name = "key", description = "User's key", in = ParameterIn.PATH, schema =
164             @Schema(type = "string"))
165     @ApiResponses({
166         @ApiResponse(responseCode = "200",
167                 description = "User successfully updated enriched with propagation status information, as Entity",
168                 content =
169                 @Content(schema =
170                         @Schema(implementation = ProvisioningResult.class))),
171         @ApiResponse(responseCode = "204",
172                 description = "No content if 'Prefer: return-no-content' was specified", headers =
173                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
174                         @Schema(type = "string"),
175                         description = "Allows the server to inform the "
176                         + "client about the fact that a specified preference was applied")) })
177     @PUT
178     @Path("{key}")
179     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
180     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
181     Response update(@NotNull UserTO user);
182 
183     /**
184      * Self-perform a status update.
185      *
186      * @param statusR status update details
187      * @return Response object featuring the updated user enriched with propagation status information
188      */
189     @Operation(security = {
190         @SecurityRequirement(name = "BasicAuthentication"),
191         @SecurityRequirement(name = "Bearer") })
192     @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
193             description = "Allows client to specify a preference for the result to be returned from the server",
194             allowEmptyValue = true, schema =
195             @Schema(defaultValue = "return-content", allowableValues = { "return-content", "return-no-content" }))
196     @Parameter(name = "key", description = "User's key", in = ParameterIn.PATH, schema =
197             @Schema(type = "string"))
198     @ApiResponses({
199         @ApiResponse(responseCode = "200",
200                 description = "User successfully updated enriched with propagation status information, as Entity",
201                 content =
202                 @Content(schema =
203                         @Schema(implementation = ProvisioningResult.class))),
204         @ApiResponse(responseCode = "204",
205                 description = "No content if 'Prefer: return-no-content' was specified", headers =
206                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
207                         @Schema(type = "string"),
208                         description = "Allows the server to inform the "
209                         + "client about the fact that a specified preference was applied")) })
210     @POST
211     @Path("{key}/status")
212     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
213     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
214     Response status(@NotNull StatusR statusR);
215 
216     /**
217      * Self-deletes user.
218      *
219      * @return Response object featuring the deleted user
220      */
221     @Operation(security = {
222         @SecurityRequirement(name = "BasicAuthentication"),
223         @SecurityRequirement(name = "Bearer") })
224     @DELETE
225     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
226     Response delete();
227 
228     /**
229      * Changes own password when change was forced by an administrator.
230      *
231      * @param password the password value to update
232      *
233      * @return Response object featuring the updated user
234      */
235     @Operation(security = {
236         @SecurityRequirement(name = "BasicAuthentication"),
237         @SecurityRequirement(name = "Bearer") })
238     @POST
239     @Path("mustChangePassword")
240     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
241     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
242     Response mustChangePassword(@NotNull PasswordPatch password);
243 
244     /**
245      * Checks compliance of the given username and / or password with applicable policies.
246      * 
247      * @param query compliance query
248      */
249     @ApiResponses(
250             @ApiResponse(responseCode = "204", description = "Operation was successful"))
251     @Operation(security = {
252         @SecurityRequirement(name = "BasicAuthentication"),
253         @SecurityRequirement(name = "Bearer") })
254     @POST
255     @Path("compliance")
256     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
257     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
258     void compliance(@NotNull ComplianceQuery query);
259 
260     /**
261      * Provides answer for the security question configured for user matching the given username, if any.
262      * If provided answer matches the one stored for that user, a password reset token is internally generated,
263      * otherwise an error is returned.
264      *
265      * @param username username for which the security answer is provided
266      * @param securityAnswer actual answer text
267      */
268     @ApiResponses(
269             @ApiResponse(responseCode = "204", description = "Operation was successful"))
270     @POST
271     @Path("requestPasswordReset")
272     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
273     void requestPasswordReset(@NotNull @QueryParam("username") String username, String securityAnswer);
274 
275     /**
276      * Reset the password value for the user matching the provided token, if available and still valid.
277      * If the token actually matches one of users, and if it is still valid at the time of submission, the matching
278      * user's password value is set as provided. The new password value will need anyway to comply with all relevant
279      * password policies.
280      *
281      * @param token password reset token
282      * @param password new password to be set
283      */
284     @ApiResponses(
285             @ApiResponse(responseCode = "204", description = "Operation was successful"))
286     @POST
287     @Path("confirmPasswordReset")
288     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
289     void confirmPasswordReset(@NotNull @QueryParam("token") String token, String password);
290 }