Merge remote-tracking branch 'server/master'

This commit is contained in:
Axel Uhl committed 2017-08-03 09:56:52 +02:00
commit 80ff2f7fe1
10 files changed
+534 -17

No files matched your search

@@ -171,7 +171,7 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
private void ensureThatUserInQuestionIsLoggedInOrCurrentUserIsAdmin(String username) throws UserManagementException {
final Subject subject = SecurityUtils.getSubject();
// the signed-in subject has role ADMIN or is changing own user
if (!subject.hasRole(DefaultRoles.ADMIN.name()) && (subject.getPrincipal() == null
if (!subject.hasRole(DefaultRoles.ADMIN.getRolename()) && (subject.getPrincipal() == null
|| !username.equals(subject.getPrincipal().toString()))) {
throw new UserManagementException(UserManagementException.INVALID_CREDENTIALS);
}
@@ -66,7 +66,10 @@ bearerToken = com.sap.sse.security.BearerTokenOrBasicOrFormAuthenticationFilter
# Specifying filter chains for URL patterns
[urls]
# Note: the following login URL does nothing but trigger the authc filter; required for Selenium testing
/api/restsecurity/login = authc
/api/restsecurity/user = bearerToken
/api/restsecurity/change_password = bearerToken
/api/restsecurity/logout = logout
/api/restsecurity/access_token = bearerToken
/api/restsecurity/remove_access_token = bearerToken
@@ -889,7 +889,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
private void ensureThatUserInQuestionIsLoggedInOrCurrentUserIsAdmin(String username) {
final Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(DefaultRoles.ADMIN.name()) && (subject.getPrincipal() == null
if (!subject.hasRole(DefaultRoles.ADMIN.getRolename()) && (subject.getPrincipal() == null
|| !username.equals(subject.getPrincipal().toString()))) {
final String currentUserName = subject.getPrincipal() == null ? "<anonymous>"
: subject.getPrincipal().toString();
@@ -1,21 +1,35 @@
package com.sap.sse.security.jaxrs.api;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.json.simple.JSONObject;
import com.sap.sse.common.Util;
import com.sap.sse.common.mail.MailException;
import com.sap.sse.security.User;
import com.sap.sse.security.jaxrs.AbstractSecurityResource;
import com.sap.sse.security.shared.DefaultRoles;
import com.sap.sse.security.shared.UserManagementException;
import com.sun.jersey.api.client.ClientResponse.Status;
@Path("/restsecurity")
public class SecurityResource extends AbstractSecurityResource {
private static final String SECURITY_UI_URL_PATH = "/security/ui/";
/**
* Can be used to figure out the current subject. Accepts the GET method. If the subject is
* authenticated, the service will respond with a "Hello &lt;subjectname&gt;" message, otherwise
@@ -48,7 +62,153 @@ public class SecurityResource extends AbstractSecurityResource {
public Response sayHelloPost() {
return doSayHello();
}
@POST
@Path("/change_password")
@Produces("text/plain;charset=UTF-8")
public Response changePassword(@FormParam("username") String username, @FormParam("password") String password) {
final Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(DefaultRoles.ADMIN.getRolename()) && (subject.getPrincipal() == null
|| !username.equals(subject.getPrincipal().toString()))) {
return Response.status(Status.UNAUTHORIZED).build();
} else {
try {
getService().updateSimpleUserPassword(username, password);
return Response.ok().build();
} catch (UserManagementException e) {
return Response.status(Status.PRECONDITION_FAILED).entity(e.getMessage()).build();
}
}
}
@POST
@Path("/forgot_password")
@Produces("text/plain;charset=UTF-8")
public Response forgotPassword(@Context UriInfo uriInfo, @QueryParam("username") String username, @QueryParam("email") String email) {
try {
final User user;
if (username != null) {
user = getService().getUserByName(username);
} else if (email != null) {
user = getService().getUserByEmail(email);
} else {
return Response.status(Status.PRECONDITION_FAILED).entity("username or email must be provided").build();
}
if (user == null) {
return Response.status(Status.PRECONDITION_FAILED).entity("user not found").build();
} else {
getService().resetPassword(user.getName(), getPasswordResetURL(uriInfo));
return Response.ok().build();
}
} catch (UserManagementException | MailException e) {
return Response.status(Status.PRECONDITION_FAILED).entity(e.getMessage()).build();
}
}
@POST
@Path("/create_user")
@Produces("text/plain;charset=UTF-8")
public Response createUser(@Context UriInfo uriInfo, @QueryParam("username") String username, @QueryParam("email") String email,
@QueryParam("password") String password, @QueryParam("fullName") String fullName,
@QueryParam("company") String company) {
try {
final String validationBaseURL = getEmailValidationBaseURL(uriInfo);
getService().createSimpleUser(username, email, password, fullName, company, validationBaseURL);
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password));
return respondWithAccessTokenForUser(username);
} catch (UserManagementException | MailException e) {
return Response.status(Status.PRECONDITION_FAILED).entity(e.getMessage()).build();
}
}
private String getEmailValidationBaseURL(UriInfo uriInfo) {
final String urlPath = SECURITY_UI_URL_PATH+"EmailValidation.html";
return getContextUrl(uriInfo, urlPath);
}
private String getPasswordResetURL(UriInfo uriInfo) {
final String urlPath = SECURITY_UI_URL_PATH+"EditProfile.html";
return getContextUrl(uriInfo, urlPath);
}
private String getContextUrl(UriInfo uriInfo, final String urlPath) {
final String validationBaseURL = uriInfo.getBaseUri().getScheme()+"://"+uriInfo.getBaseUri().getHost()+
(uriInfo.getBaseUri().getPort() == -1 ? "" : (":"+uriInfo.getBaseUri().getPort()))+urlPath;
return validationBaseURL;
}
@GET
@Path("/user")
@Produces("application/json;charset=UTF-8")
public Response getUser(@QueryParam("username") String username) {
final Subject subject = SecurityUtils.getSubject();
// ADMIN can query all; otherwise, only the owning user can query
// TODO: ideally, we would introduce a USER:READ:<username> permission which later can be granted to tenant admins for all users of that tenant
if (subject.getPrincipal() == null || (username != null && !subject.hasRole(DefaultRoles.ADMIN.getRolename()))) {
return Response.status(Status.UNAUTHORIZED).build();
} else {
final User user = getService().getUserByName(username == null ? subject.getPrincipal().toString() : username);
if (user == null) {
return Response.status(Status.PRECONDITION_FAILED).entity("User "+username+" not known").build();
} else {
JSONObject result = new JSONObject();
result.put("username", username);
result.put("fullName", user.getFullName());
result.put("email", user.getEmail());
result.put("company", user.getCompany());
return Response.ok(result.toJSONString()).build();
}
}
}
@DELETE
@Path("/user")
@Produces("text/plain;charset=UTF-8")
public Response deleteUser(@QueryParam("username") String username) {
final Subject subject = SecurityUtils.getSubject();
// the signed-in subject has role ADMIN
if (!subject.hasRole(DefaultRoles.ADMIN.getRolename()) && (subject.getPrincipal() == null
|| !username.equals(subject.getPrincipal().toString()))) {
return Response.status(Status.UNAUTHORIZED).build();
} else {
try {
getService().deleteUser(username);
return Response.ok().build();
} catch (UserManagementException e) {
return Response.status(Status.PRECONDITION_FAILED).entity(e.getMessage()).build();
}
}
}
@PUT
@Path("/user")
@Produces("text/plain;charset=UTF-8")
public Response updateUser(@Context UriInfo uriInfo, @QueryParam("username") String username,
@QueryParam("email") String email, @QueryParam("fullName") String fullName,
@QueryParam("company") String company) {
final Subject subject = SecurityUtils.getSubject();
// the signed-in subject has role ADMIN
if (!subject.hasRole(DefaultRoles.ADMIN.getRolename()) && (subject.getPrincipal() == null
|| !username.equals(subject.getPrincipal().toString()))) {
return Response.status(Status.UNAUTHORIZED).build();
} else {
try {
final User user = getService().getUserByName(username);
if (user == null) {
return Response.status(Status.PRECONDITION_FAILED).entity("User "+username+" not known").build();
} else {
getService().updateUserProperties(username, fullName, company, user.getLocale());
if (!Util.equalsWithNull(user.getEmail(), email)) {
getService().updateSimpleUserEmail(username, email, getEmailValidationBaseURL(uriInfo));
}
return Response.ok().build();
}
} catch (UserManagementException e) {
return Response.status(Status.PRECONDITION_FAILED).entity(e.getMessage()).build();
}
}
}
@GET
@Path("/access_token")
@Produces("application/json;charset=UTF-8")
@@ -62,6 +222,21 @@ public class SecurityResource extends AbstractSecurityResource {
public Response accessTokenPost() {
return respondWithAccessTokenForAuthenticatedSubject();
}
@GET
@Path("/logout")
@Produces("application/json;charset=UTF-8")
public Response logout() {
return logoutPost();
}
@POST
@Path("/logout")
@Produces("application/json;charset=UTF-8")
public Response logoutPost() {
getService().logout();
return Response.ok().build();
}
@GET
@Path("/remove_access_token")
@@ -45,7 +45,7 @@ shorthand URL notations with ":" and "@" to denote basic authentication, with th
<code>curl -D /tmp/header -b JSESSIONID=5e94c93e-b515-4bcd-8df8-0c786805a9e0 http://127.0.0.1:8888/security/api/restsecurity/access_token</code>
</div>
<div>Using POST username and password form parameters:<br>
<code>curl -d "username=admin&password=admin"-D /tmp/header http://127.0.0.1:8888/security/api/restsecurity/access_token</code>
<code>curl -d "username=admin&password=admin" -D /tmp/header http://127.0.0.1:8888/security/api/restsecurity/access_token</code>
</div>
<div>Using a bearer access token:<br>
<code>curl -D /tmp/header -H "Authorization: Bearer MVPg/uCV1QGqMDmDtUuOQBrSrB1NlIldm0LW+dnxZ2A=" http://127.0.0.1:8888/security/api/restsecurity/access_token</code>
@@ -9,12 +9,11 @@
</head>
<body>
<h1>SAP Sponsorships Engine Security Webservices API</h1>
<h2>URL: /api/restsecurity/login</h2>
<h2>URL: /api/restsecurity/change_password</h2>
<b>Description:</b>
<p>The service authenticates a subject based on POST form parameter and creates a session.
The session is represented by the JSESSIONID cookie value which can be used in subsequent
requests to the same session with the same authenticated subject.</p>
<p>The service updates a user's password. This requires that user to be the currently authenticated
subject or to have administrator permissions.</p>
<br/>
<table>
<tr>
@@ -23,8 +22,7 @@
</tr>
<tr>
<td>Output format:</td>
<td>Redirect (302 Found) to /api/restsecurity/hello which delivers a JSON document of the following format<br>
<pre>{"principal":"admin","authenticated":true,"remembered":false}</pre>
<td>An Empty response with status code 200 in case of success; error-indicating status codes otherwise</pre>
</td>
</tr>
<tr>
@@ -36,8 +34,8 @@
</tr>
<tr>
<td>Example:</td>
<td><code>curl -L -D /tmp/header -d "username=admin&password=admin" http://127.0.0.1:8888/security/api/restsecurity/login</code><br>
Will provide a redirect and a JSON document as explained above.</td>
<td><code>curl -X POST -d "username=uhl&password=123" "http://127.0.0.1:8888/security/api/restsecurity/user"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
</body>
+120
View File
@@ -0,0 +1,120 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>SAP Sponsorships Engine Security Webservices API</title>
</head>
<body>
<h1>SAP Sponsorships Engine Security Webservices API</h1>
<h2>URL: /api/restsecurity/create_user</h2>
<b>Description:</b>
<p>The service creates a user object.</p>
<br/>
<table>
<tr>
<td>Request Method:</td>
<td>POST</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>password</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&password=abcabc&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>PUT</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>password</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&password=abcabc&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>PUT</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>password</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&password=abcabc&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>DELETE</td>
</tr>
<tr>
<td>Output format:</td>
<td>An Empty response with status code 200 in case of success; error-indicating status codes otherwise</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X DELETE "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl"</code></td>
</tr>
</table>
</body>
</html>
@@ -0,0 +1,118 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>SAP Sponsorships Engine Security Webservices API</title>
</head>
<body>
<h1>SAP Sponsorships Engine Security Webservices API</h1>
<h2>URL: /api/restsecurity/forgot_password</h2>
<b>Description:</b>
<p>Allows password recovery by means of resetting the current password and sending an e-mail
with a recovery link, all possible only if the user has a validated e-mail address.</p>
<br/>
<table>
<tr>
<td>Request Method:</td>
<td>POST</td>
</tr>
<tr>
<td>Output format:</td>
<td>Output format:</td>
<td>An Empty response with status code 200 in case of success; error-indicating status codes otherwise</pre>
</td>
</tr>
<tr>
<td>One of <code>username</code> and <code>email</code> must be specified:</td>
<td>
<div>username</div>
<div>email</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X POST "http://127.0.0.1:8888/security/api/restsecurity/forgot_password?username=uhl"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>PUT</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>password</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&password=abcabc&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>PUT</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>password</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&password=abcabc&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>DELETE</td>
</tr>
<tr>
<td>Output format:</td>
<td>An Empty response with status code 200 in case of success; error-indicating status codes otherwise</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X DELETE "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl"</code></td>
</tr>
</table>
</body>
</html>
@@ -27,20 +27,32 @@ be <code>http://sapsailing.com/security/api/restsecurity/login</code>.
</thead>
<tbody>
<tr>
<td><a href="login.html">/api/restsecurity/login</a></td>
<td>Log in a subject</td>
<td><a href="user.html">/api/restsecurity/create_user</a></td>
<td>Create a user</td>
</tr>
<tr>
<td><a href="logout.html">/api/restsecurity/logout</a></td>
<td>Log out a subject</td>
<td><a href="user.html">/api/restsecurity/change_password</a></td>
<td>Change a user's password</td>
</tr>
<tr>
<td><a href="user.html">/api/restsecurity/forgot_password</a></td>
<td>Trigger the process of password recovery</td>
</tr>
<tr>
<td><a href="user.html">/api/restsecurity/user</a></td>
<td>Read, update or delete a user</td>
</tr>
<tr>
<td><a href="access_token.html">/api/restsecurity/access_token</a></td>
<td>Obtain a bearer access token</td>
</tr>
<tr>
<td><a href="hello.html">/api/restsecurity/hello</a></td>
<td>Test that a subject is authenticated</td>
</tr>
<tr>
<td><a href="access_token">/api/restsecurity/access_token</a></td>
<td>Obtain a bearer access token</td>
<td><a href="remove_access_token.html">/api/restsecurity/remove_access_token</a></td>
<td>Remove an existing access token, rendering it invalid for authenticating to the server</td>
</tr>
</tbody>
</table>
+91
View File
@@ -0,0 +1,91 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>SAP Sponsorships Engine Security Webservices API</title>
</head>
<body>
<h1>SAP Sponsorships Engine Security Webservices API</h1>
<h2>URL: /api/restsecurity/user</h2>
<b>Description:</b>
<p>The service creates reads, updates or deletes a user object, based on the HTTP method used. This endpoint
cannot be used to update the password. See <a href="change_password.html"><code>change_password</code></a>.</p>
<br/>
<table>
<tr>
<td>Request Method:</td>
<td>PUT</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format, documenting the successful creation of the user object<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
<div>email</div>
<div>fullName</div>
<div>company</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X PUT "http://127.0.0.1:8888/security/api/restsecurity/user?username=uhl&email=axel.uhl@sap.com&fullName=Axel%20Uhl&company=SAP%20SE"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>GET</td>
</tr>
<tr>
<td>Output format:</td>
<td>a JSON document of the following format<br>
<pre>{"username":"abc","access_token":"gISIhDNIFoXEVxzefrKUcXYOFKwBfq1xDPfv4wQdDjA="}</pre>
</td>
</tr>
<tr>
<td>Optional parameter:</td>
<td>
<div>username</div> If provided, the record for the user with that name will be shown. This requires this
user to be the currently authenticated subject, or the authenticated subject needs to
have administrator permissions.
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl "http://admin:admin@127.0.0.1:8888/security/api/restsecurity/user?username=uhl"</code><br>
Will provide a JSON document as explained above.</td>
</tr>
</table>
<table>
<tr>
<td>Request Method:</td>
<td>DELETE</td>
</tr>
<tr>
<td>Output format:</td>
<td>An Empty response with status code 200 in case of success; error-indicating status codes otherwise</pre>
</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>
<div>username</div>
</td>
</tr>
<tr>
<td>Example:</td>
<td><code>curl -X DELETE "http://admin:admin@127.0.0.1:8888/security/api/restsecurity/user?username=uhl"</code></td>
</tr>
</table>
</body>
</html>