KEYCLOAK-4146 Admin CLI enhancements
- Added -b, --body and composite roles support - Added a test that performs a demo session
This commit is contained in:
parent
d217b23719
commit
9ab44b79ce
9 changed files with 463 additions and 46 deletions
|
@ -73,6 +73,8 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
|
||||
String file;
|
||||
|
||||
String body;
|
||||
|
||||
String fields;
|
||||
|
||||
boolean printHeaders;
|
||||
|
@ -210,6 +212,10 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
throw new IllegalArgumentException("Options --merge and --no-merge are mutually exclusive");
|
||||
}
|
||||
|
||||
if (body != null && file != null) {
|
||||
throw new IllegalArgumentException("Options --body and --file are mutually exclusive");
|
||||
}
|
||||
|
||||
if (file == null && attrs.size() > 0 && !noMerge) {
|
||||
mergeMode = true;
|
||||
}
|
||||
|
@ -222,17 +228,17 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
// see if Content-Type header is explicitly set to non-json value
|
||||
Header ctype = headers.get("content-type");
|
||||
|
||||
InputStream body = null;
|
||||
InputStream content = null;
|
||||
|
||||
CmdStdinContext<JsonNode> ctx = new CmdStdinContext<>();
|
||||
|
||||
if (file != null) {
|
||||
if (ctype != null && !"application/json".equals(ctype.getValue())) {
|
||||
if ("-".equals(file)) {
|
||||
body = System.in;
|
||||
content = System.in;
|
||||
} else {
|
||||
try {
|
||||
body = new BufferedInputStream(new FileInputStream(file));
|
||||
content = new BufferedInputStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("File not found: " + file);
|
||||
}
|
||||
|
@ -240,6 +246,8 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
} else {
|
||||
ctx = parseFileOrStdin(file);
|
||||
}
|
||||
} else if (body != null) {
|
||||
content = new ByteArrayInputStream(body.getBytes(Charset.forName("utf-8")));
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
|
@ -304,15 +312,15 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
}
|
||||
|
||||
if (attrs.size() > 0) {
|
||||
if (body != null) {
|
||||
if (content != null) {
|
||||
throw new RuntimeException("Can't set attributes on content of type other than application/json");
|
||||
}
|
||||
|
||||
ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
||||
}
|
||||
|
||||
if (body == null && ctx.getContent() != null) {
|
||||
body = new ByteArrayInputStream(ctx.getContent().getBytes(Charset.forName("utf-8")));
|
||||
if (content == null && ctx.getContent() != null) {
|
||||
content = new ByteArrayInputStream(ctx.getContent().getBytes(Charset.forName("utf-8")));
|
||||
}
|
||||
|
||||
ReturnFields returnFields = null;
|
||||
|
@ -322,7 +330,7 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
}
|
||||
|
||||
// make sure content type is set
|
||||
if (body != null) {
|
||||
if (content != null) {
|
||||
headers.addIfMissing("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
|
@ -339,7 +347,7 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
|||
|
||||
HeadersBodyStatus response;
|
||||
try {
|
||||
response = HttpUtil.doRequest(httpVerb, resourceUrl, new HeadersBody(headers, body));
|
||||
response = HttpUtil.doRequest(httpVerb, resourceUrl, new HeadersBody(headers, content));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("HTTP request error: " + e.getMessage(), e);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,12 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
@Option(name = "gid", description = "Target group's 'id'")
|
||||
String gid;
|
||||
|
||||
@Option(name = "rname", description = "Composite role's 'name'")
|
||||
String rname;
|
||||
|
||||
@Option(name = "rid", description = "Composite role's 'id'")
|
||||
String rid;
|
||||
|
||||
@Option(name = "cclientid", description = "Target client's 'clientId'")
|
||||
String cclientid;
|
||||
|
||||
|
@ -116,19 +122,31 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
}
|
||||
|
||||
if (roleNames.isEmpty() && roleIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("No role specified. Use --rolename or --roleid to specify roles");
|
||||
throw new IllegalArgumentException("No role to add specified. Use --rolename or --roleid to specify roles to add");
|
||||
}
|
||||
|
||||
if (cid != null && cclientid != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --cid and --cclientid are mutually exclusive");
|
||||
}
|
||||
|
||||
if (rid != null && rname != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rid and --rname are mutually exclusive");
|
||||
}
|
||||
|
||||
if (isUserSpecified() && isGroupSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (!isUserSpecified() && !isGroupSpecified()) {
|
||||
throw new IllegalArgumentException("No user nor group specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group");
|
||||
if (isUserSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --rname / --rid");
|
||||
}
|
||||
|
||||
if (isGroupSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rname / --rid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (!isUserSpecified() && !isGroupSpecified() && !isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("No user nor group nor composite role specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group or --rname / --rid to specify a composite role");
|
||||
}
|
||||
|
||||
|
||||
|
@ -204,9 +222,32 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
GroupOperations.addRealmRoles(adminRoot, realm, auth, gid, new ArrayList<>(rolesToAdd));
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if (isCompositeRoleSpecified()) {
|
||||
if (rid == null) {
|
||||
rid = RoleOperations.getIdFromRoleName(adminRoot, realm, auth, rname);
|
||||
}
|
||||
if (isClientSpecified()) {
|
||||
// list client roles for a composite role
|
||||
if (cid == null) {
|
||||
cid = ClientOperations.getIdFromClientId(adminRoot, realm, auth, cclientid);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No user nor group specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group");
|
||||
List<ObjectNode> roles = RoleOperations.getClientRoles(adminRoot, realm, cid, auth);
|
||||
Set<ObjectNode> rolesToAdd = getRoleRepresentations(roleNames, roleIds, new LocalSearch(roles));
|
||||
|
||||
// now add all the roles
|
||||
RoleOperations.addClientRoles(adminRoot, realm, auth, rid, new ArrayList<>(rolesToAdd));
|
||||
|
||||
} else {
|
||||
Set<ObjectNode> rolesToAdd = getRoleRepresentations(roleNames, roleIds,
|
||||
new LocalSearch(RoleOperations.getRealmRolesAsNodes(adminRoot, realm, auth)));
|
||||
|
||||
// now add all the roles
|
||||
RoleOperations.addRealmRoles(adminRoot, realm, auth, rid, new ArrayList<>(rolesToAdd));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("No user nor group, nor composite role specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group or --rname / --rid to specify a composite role");
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
|
@ -257,6 +298,9 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
return uid != null || uusername != null;
|
||||
}
|
||||
|
||||
private boolean isCompositeRoleSpecified() {
|
||||
return rid != null || rname != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
|
@ -275,9 +319,10 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
StringWriter sb = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(sb);
|
||||
out.println("Usage: " + CMD + " add-roles (--uusername USERNAME | --uid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println("Usage: " + CMD + " add-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println(" " + CMD + " add-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println(" " + CMD + " add-roles (--rname ROLE_NAME | --rid ROLE_ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println();
|
||||
out.println("Command to add realm or client roles to a user or group.");
|
||||
out.println("Command to add realm or client roles to a user, a group or a composite role.");
|
||||
out.println();
|
||||
out.println("Use `" + CMD + " config credentials` to establish an authenticated session, or use CREDENTIALS OPTIONS");
|
||||
out.println("to perform one time authentication.");
|
||||
|
@ -285,7 +330,8 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
out.println("If client is specified using --cclientid or --cid then roles to add are client roles, otherwise they are realm roles.");
|
||||
out.println("Either a user, or a group needs to be specified. If user is specified using --uusername or --uid then roles are added");
|
||||
out.println("to a specific user. If group is specified using --gname, --gpath or --gid then roles are added to a specific group.");
|
||||
out.println("One or more roles have to be specified using --rolename or --roleid so that they are added to a group or a user.");
|
||||
out.println("If composite role is specified using --rname or --rid then roles are added to a specific composite role.");
|
||||
out.println("One or more roles have to be specified using --rolename or --roleid so that they are added to a group, a user or a composite role.");
|
||||
out.println();
|
||||
out.println("Arguments:");
|
||||
out.println();
|
||||
|
@ -306,6 +352,8 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
|||
out.println(" to use --gid, or --gpath to specify the target group");
|
||||
out.println(" --gpath Group's 'path' attribute");
|
||||
out.println(" --gid Group's 'id' attribute");
|
||||
out.println(" --rname Composite role's 'name' attribute");
|
||||
out.println(" --rid Composite role's 'id' attribute");
|
||||
out.println(" --cclientid Client's 'clientId' attribute");
|
||||
out.println(" --cid Client's 'id' attribute");
|
||||
out.println(" --rolename Role's 'name' attribute");
|
||||
|
|
|
@ -34,9 +34,12 @@ import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|||
@CommandDefinition(name = "create", description = "Command to create new resources")
|
||||
public class CreateCmd extends AbstractRequestCmd {
|
||||
|
||||
@Option(shortName = 'f', name = "file", description = "Read object from file or standard input if FILENAME is set to '-'", hasValue = true)
|
||||
@Option(shortName = 'f', name = "file", description = "Read object from file or standard input if FILENAME is set to '-'")
|
||||
String file;
|
||||
|
||||
@Option(shortName = 'b', name = "body", description = "JSON object to be sent as-is or used as a template")
|
||||
String body;
|
||||
|
||||
@Option(shortName = 'F', name = "fields", description = "A pattern specifying which attributes of JSON response body to actually display as result - causes mismatch with Content-Length header", hasValue = true)
|
||||
String fields;
|
||||
|
||||
|
@ -59,6 +62,7 @@ public class CreateCmd extends AbstractRequestCmd {
|
|||
void initOptions() {
|
||||
// set options on parent
|
||||
super.file = file;
|
||||
super.body = body;
|
||||
super.fields = fields;
|
||||
super.printHeaders = printHeaders;
|
||||
super.returnId = returnId;
|
||||
|
@ -69,7 +73,7 @@ public class CreateCmd extends AbstractRequestCmd {
|
|||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && file == null && (args == null || args.size() == 0);
|
||||
return noOptions() && file == null && body == null && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
|
@ -109,6 +113,7 @@ public class CreateCmd extends AbstractRequestCmd {
|
|||
out.println(" -s, --set NAME=VALUE Set a specific attribute NAME to a specified value VALUE");
|
||||
out.println(" -d, --delete NAME Remove a specific attribute NAME from JSON request body");
|
||||
out.println(" -f, --file FILENAME Read object from file or standard input if FILENAME is set to '-'");
|
||||
out.println(" -b, --body CONTENT Content to be sent as-is or used as a JSON object template");
|
||||
out.println(" -q, --query NAME=VALUE Add to request URI a NAME query parameter with value VALUE");
|
||||
out.println(" -h, --header NAME=VALUE Set request header NAME to VALUE");
|
||||
out.println();
|
||||
|
@ -153,8 +158,19 @@ public class CreateCmd extends AbstractRequestCmd {
|
|||
out.println(" EOF");
|
||||
}
|
||||
out.println();
|
||||
out.println("Create a new group using configuration JSON passed as 'body' argument:");
|
||||
if (OS_ARCH.isWindows()) {
|
||||
out.println(" " + PROMPT + " " + CMD + " create groups -r demorealm -b \"{ \\\"name\\\": \\\"Admins\\\" }\"");
|
||||
} else {
|
||||
out.println(" " + PROMPT + " " + CMD + " create groups -r demorealm -b '{ \"name\": \"Admins\" }'");
|
||||
}
|
||||
out.println();
|
||||
out.println("Create a client using file as a template, and override some attributes - return an 'id' of new client:");
|
||||
out.println(" " + PROMPT + " " + CMD + " create clients -r demorealm -f my_client.json -s clientId=my_client2 -s 'redirectUris=[\"http://localhost:8980/myapp/*\"]' -i");
|
||||
if (OS_ARCH.isWindows()) {
|
||||
out.println(" " + PROMPT + " " + CMD + " create clients -r demorealm -f my_client.json -s clientId=my_client2 -s \"redirectUris=[\\\"http://localhost:8980/myapp/*\\\"]\" -i");
|
||||
} else {
|
||||
out.println(" " + PROMPT + " " + CMD + " create clients -r demorealm -f my_client.json -s clientId=my_client2 -s 'redirectUris=[\"http://localhost:8980/myapp/*\"]' -i");
|
||||
}
|
||||
out.println();
|
||||
out.println("Create a new client role for client my_client in realm 'demorealm' (replace ID with output of previous example command):");
|
||||
out.println(" " + PROMPT + " " + CMD + " create clients/ID/roles -r demorealm -s name=client_role");
|
||||
|
|
|
@ -80,6 +80,7 @@ public class DeleteCmd extends CreateCmd {
|
|||
out.println(" -s, --set NAME=VALUE Send a body with request - set a specific attribute NAME to a specified value VALUE");
|
||||
out.println(" -d, --delete NAME Remove a specific attribute NAME from JSON request body");
|
||||
out.println(" -f, --file FILENAME Send a body with request - read object from file or standard input if FILENAME is set to '-'");
|
||||
out.println(" -b, --body CONTENT Content to be sent as-is or used as a JSON object template");
|
||||
out.println(" -q, --query NAME=VALUE Add to request URI a NAME query parameter with value VALUE");
|
||||
out.println(" -h, --header NAME=VALUE Set request header NAME to VALUE");
|
||||
out.println();
|
||||
|
|
|
@ -57,10 +57,10 @@ public class GetRolesCmd extends GetCmd {
|
|||
@Option(name = "cid", description = "Target client's 'id'")
|
||||
String cid;
|
||||
|
||||
@Option(name = "rolename", description = "Target role's 'name'")
|
||||
@Option(name = "rname", description = "Composite role's 'name'")
|
||||
String rname;
|
||||
|
||||
@Option(name = "roleid", description = "Target role's 'id'")
|
||||
@Option(name = "rid", description = "Composite role's 'id'")
|
||||
String rid;
|
||||
|
||||
@Option(name = "gname", description = "Target group's 'name'")
|
||||
|
@ -72,6 +72,12 @@ public class GetRolesCmd extends GetCmd {
|
|||
@Option(name = "gid", description = "Target group's 'id'")
|
||||
String gid;
|
||||
|
||||
@Option(name = "rolename", description = "Target role's 'name'")
|
||||
String rolename;
|
||||
|
||||
@Option(name = "roleid", description = "Target role's 'id'")
|
||||
String roleid;
|
||||
|
||||
@Option(name = "available", description = "List only available roles", hasValue = false)
|
||||
boolean available;
|
||||
|
||||
|
@ -108,10 +114,14 @@ public class GetRolesCmd extends GetCmd {
|
|||
throw new IllegalArgumentException("Incompatible options: --gid, --gname and --gpath are mutually exclusive");
|
||||
}
|
||||
|
||||
if (rid != null && rname != null) {
|
||||
if (roleid != null && rolename != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --roleid and --rolename are mutually exclusive");
|
||||
}
|
||||
|
||||
if (rid != null && rname != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rid and --rname are mutually exclusive");
|
||||
}
|
||||
|
||||
if (cid != null && cclientid != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --cid and --cclientid are mutually exclusive");
|
||||
}
|
||||
|
@ -120,6 +130,22 @@ public class GetRolesCmd extends GetCmd {
|
|||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (isUserSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --rname / --rid");
|
||||
}
|
||||
|
||||
if (isGroupSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rname / --rid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (all && effective) {
|
||||
throw new IllegalArgumentException("Incompatible options: --all can't be used at the same time as --effective");
|
||||
}
|
||||
|
||||
if (all && available) {
|
||||
throw new IllegalArgumentException("Incompatible options: --all can't be used at the same time as --available");
|
||||
}
|
||||
|
||||
super.processOptions(commandInvocation);
|
||||
}
|
||||
|
||||
|
@ -167,7 +193,7 @@ public class GetRolesCmd extends GetCmd {
|
|||
} else if (effective) {
|
||||
super.url = composeResourceUrl(adminRoot, realm, "users/" + uid + "/role-mappings/realm/composite");
|
||||
} else {
|
||||
super.url = composeResourceUrl(adminRoot, realm, "users/" + uid + "/role-mappings/realm");
|
||||
super.url = composeResourceUrl(adminRoot, realm, "users/" + uid + (all ? "/role-mappings" : "/role-mappings/realm"));
|
||||
}
|
||||
}
|
||||
} else if (isGroupSpecified()) {
|
||||
|
@ -195,9 +221,35 @@ public class GetRolesCmd extends GetCmd {
|
|||
} else if (effective) {
|
||||
super.url = composeResourceUrl(adminRoot, realm, "groups/" + gid + "/role-mappings/realm/composite");
|
||||
} else {
|
||||
super.url = composeResourceUrl(adminRoot, realm, "groups/" + gid + "/role-mappings/realm");
|
||||
super.url = composeResourceUrl(adminRoot, realm, "groups/" + gid + (all ? "/role-mappings" : "/role-mappings/realm"));
|
||||
}
|
||||
}
|
||||
} else if (isCompositeRoleSpecified()) {
|
||||
String uri = rname != null ? "roles/" + rname : "roles-by-id/" + rid;
|
||||
|
||||
if (isClientSpecified()) {
|
||||
if (cid == null) {
|
||||
cid = ClientOperations.getIdFromClientId(adminRoot, realm, auth, cclientid);
|
||||
}
|
||||
if (available) {
|
||||
throw new IllegalArgumentException("Option --available not supported with composite roles. Try '" + CMD + " get-roles --cid " + cid + "' for full list of client roles for that client");
|
||||
}
|
||||
if (effective) {
|
||||
throw new IllegalArgumentException("Option --effective not supported with composite roles.");
|
||||
}
|
||||
uri += "/composites/clients/" + cid;
|
||||
} else {
|
||||
if (available) {
|
||||
throw new IllegalArgumentException("Option --available not supported with composite roles. Try '" + CMD + " get-roles' for full list of realm roles");
|
||||
}
|
||||
if (effective) {
|
||||
throw new IllegalArgumentException("Option --effective not supported with composite roles.");
|
||||
}
|
||||
|
||||
uri += all ? "/composites" : "/composites/realm";
|
||||
}
|
||||
super.url = composeResourceUrl(adminRoot, realm, uri);
|
||||
|
||||
} else if (isClientSpecified()) {
|
||||
if (cid == null) {
|
||||
cid = ClientOperations.getIdFromClientId(adminRoot, realm, auth, cclientid);
|
||||
|
@ -205,10 +257,10 @@ public class GetRolesCmd extends GetCmd {
|
|||
|
||||
if (isRoleSpecified()) {
|
||||
// get specific client role
|
||||
if (rname == null) {
|
||||
rname = RoleOperations.getClientRoleNameFromId(adminRoot, realm, auth, cid, rid);
|
||||
if (rolename == null) {
|
||||
rolename = RoleOperations.getClientRoleNameFromId(adminRoot, realm, auth, cid, roleid);
|
||||
}
|
||||
super.url = composeResourceUrl(adminRoot, realm, "clients/" + cid + "/roles/" + rname);
|
||||
super.url = composeResourceUrl(adminRoot, realm, "clients/" + cid + "/roles/" + rolename);
|
||||
} else {
|
||||
// list defined client roles
|
||||
super.url = composeResourceUrl(adminRoot, realm, "clients/" + cid + "/roles");
|
||||
|
@ -216,10 +268,10 @@ public class GetRolesCmd extends GetCmd {
|
|||
} else {
|
||||
if (isRoleSpecified()) {
|
||||
// get specific realm role
|
||||
if (rname == null) {
|
||||
rname = RoleOperations.getClientRoleNameFromId(adminRoot, realm, auth, cid, rid);
|
||||
if (rolename == null) {
|
||||
rolename = RoleOperations.getClientRoleNameFromId(adminRoot, realm, auth, cid, roleid);
|
||||
}
|
||||
super.url = composeResourceUrl(adminRoot, realm, "roles/" + rname);
|
||||
super.url = composeResourceUrl(adminRoot, realm, "roles/" + rolename);
|
||||
} else {
|
||||
// list defined realm roles
|
||||
super.url = composeResourceUrl(adminRoot, realm, "roles");
|
||||
|
@ -230,7 +282,7 @@ public class GetRolesCmd extends GetCmd {
|
|||
}
|
||||
|
||||
private boolean isRoleSpecified() {
|
||||
return rid != null || rname != null;
|
||||
return roleid != null || rolename != null;
|
||||
}
|
||||
|
||||
private boolean isClientSpecified() {
|
||||
|
@ -241,6 +293,10 @@ public class GetRolesCmd extends GetCmd {
|
|||
return gid != null || gname != null || gpath != null;
|
||||
}
|
||||
|
||||
private boolean isCompositeRoleSpecified() {
|
||||
return rid != null || rname != null;
|
||||
}
|
||||
|
||||
private boolean isUserSpecified() {
|
||||
return uid != null || uusername != null;
|
||||
}
|
||||
|
@ -261,10 +317,11 @@ public class GetRolesCmd extends GetCmd {
|
|||
StringWriter sb = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(sb);
|
||||
out.println("Usage: " + CMD + " get-roles [--cclientid CLIENT_ID | --cid ID] [ARGUMENTS]");
|
||||
out.println("Usage: " + CMD + " get-roles (--uusername USERNAME | --uid ID) [--cclientid CLIENT_ID | --cid ID] [--available | --effective] (ARGUMENTS)");
|
||||
out.println("Usage: " + CMD + " get-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] [--available | --effective] [ARGUMENTS]");
|
||||
out.println(" " + CMD + " get-roles (--uusername USERNAME | --uid ID) [--cclientid CLIENT_ID | --cid ID] [--available | --effective | --all] (ARGUMENTS)");
|
||||
out.println(" " + CMD + " get-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] [--available | --effective | --all] [ARGUMENTS]");
|
||||
out.println(" " + CMD + " get-roles (--rname ROLE_NAME | --rid ROLE_ID) [--cclientid CLIENT_ID | --cid ID] [--available | --effective | --all] [ARGUMENTS]");
|
||||
out.println();
|
||||
out.println("Command to list realm or client roles on a realm, user or group.");
|
||||
out.println("Command to list realm or client roles of a realm, a user, a group or a composite role.");
|
||||
out.println();
|
||||
out.println("Use `" + CMD + " config credentials` to establish an authenticated session, or use CREDENTIALS OPTIONS");
|
||||
out.println("to perform one time authentication.");
|
||||
|
@ -272,11 +329,13 @@ public class GetRolesCmd extends GetCmd {
|
|||
out.println("If client is specified using --cclientid or --cid then client roles are listed, otherwise realm roles are listed.");
|
||||
out.println("If user is specified using --uusername or --uid then roles are listed for a specific user.");
|
||||
out.println("If group is specified using --gname, --gpath or --gid then roles are listed for a specific group.");
|
||||
out.println("If neither user nor group is specified then defined roles are listed for a realm or specific client");
|
||||
out.println("If composite role is specified --rname or --rid then roles are listed for a specific composite role.");
|
||||
out.println("If neither user nor group, nor composite role is specified then defined roles are listed for a realm or specific client.");
|
||||
out.println("If role is specified using --rolename or --roleid then only that specific role is returned.");
|
||||
out.println("If --available is specified, then only roles not yet added to the target user or group are returned.");
|
||||
out.println("If --effective is specified, then roles added to the target user or group are transitively resolved and a full");
|
||||
out.println("set of roles in effect for that user or group is returned.");
|
||||
out.println("set of roles in effect for that user, group or composite role is returned.");
|
||||
out.println("If --all is specified, then client roles for all clients are returned in addition to realm roles.");
|
||||
out.println();
|
||||
out.println("Arguments:");
|
||||
out.println();
|
||||
|
@ -297,10 +356,15 @@ public class GetRolesCmd extends GetCmd {
|
|||
out.println(" to use --gid, or --gpath to specify the target group");
|
||||
out.println(" --gpath Group's 'path' attribute");
|
||||
out.println(" --gid Group's 'id' attribute");
|
||||
out.println(" --rname Composite role's 'name' attribute");
|
||||
out.println(" --rid Composite role's 'id' attribute");
|
||||
out.println(" --cclientid Client's 'clientId' attribute");
|
||||
out.println(" --cid Client's 'id' attribute");
|
||||
out.println(" --rolename Role's 'name' attribute");
|
||||
out.println(" --roleid Role's 'id' attribute");
|
||||
out.println(" --available Return available roles - those that can still be added");
|
||||
out.println(" --effective Return effective roles - transitively taking composite roles into account");
|
||||
out.println(" --all Return all client roles in addition to realm roles");
|
||||
out.println(" -a, --admin-root URL URL of Admin REST endpoint root if not default - e.g. http://localhost:8080/auth/admin");
|
||||
out.println(" -r, --target-realm REALM Target realm to issue requests against if not the one authenticated against");
|
||||
out.println();
|
||||
|
|
|
@ -67,6 +67,12 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
@Option(name = "gid", description = "Target group's 'id'")
|
||||
String gid;
|
||||
|
||||
@Option(name = "rname", description = "Composite role's 'name'")
|
||||
String rname;
|
||||
|
||||
@Option(name = "rid", description = "Composite role's 'id'")
|
||||
String rid;
|
||||
|
||||
@Option(name = "cclientid", description = "Target client's 'clientId'")
|
||||
String cclientid;
|
||||
|
||||
|
@ -116,19 +122,31 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
}
|
||||
|
||||
if (roleNames.isEmpty() && roleIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("No role specified. Use --rolename or --roleid to specify roles");
|
||||
throw new IllegalArgumentException("No role to remove specified. Use --rolename or --roleid to specify roles to remove");
|
||||
}
|
||||
|
||||
if (cid != null && cclientid != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --cid and --cclientid are mutually exclusive");
|
||||
}
|
||||
|
||||
if (rid != null && rname != null) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rid and --rname are mutually exclusive");
|
||||
}
|
||||
|
||||
if (isUserSpecified() && isGroupSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (!isUserSpecified() && !isGroupSpecified()) {
|
||||
throw new IllegalArgumentException("No user nor group specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group");
|
||||
if (isUserSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --uusername / --uid can't be used at the same time as --rname / --rid");
|
||||
}
|
||||
|
||||
if (isGroupSpecified() && isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("Incompatible options: --rname / --rid can't be used at the same time as --gname / --gid / --gpath");
|
||||
}
|
||||
|
||||
if (!isUserSpecified() && !isGroupSpecified() && !isCompositeRoleSpecified()) {
|
||||
throw new IllegalArgumentException("No user nor group nor composite role specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group or --rname / --rid to specify a composite role");
|
||||
}
|
||||
|
||||
|
||||
|
@ -204,9 +222,32 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
GroupOperations.removeRealmRoles(adminRoot, realm, auth, gid, new ArrayList<>(rolesToAdd));
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if (isCompositeRoleSpecified()) {
|
||||
if (rid == null) {
|
||||
rid = RoleOperations.getIdFromRoleName(adminRoot, realm, auth, rname);
|
||||
}
|
||||
if (isClientSpecified()) {
|
||||
// remove client roles from a role
|
||||
if (cid == null) {
|
||||
cid = ClientOperations.getIdFromClientId(adminRoot, realm, auth, cclientid);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No user nor group specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group");
|
||||
List<ObjectNode> roles = RoleOperations.getClientRoles(adminRoot, realm, cid, auth);
|
||||
Set<ObjectNode> rolesToAdd = getRoleRepresentations(roleNames, roleIds, new LocalSearch(roles));
|
||||
|
||||
// now remove the roles
|
||||
RoleOperations.removeClientRoles(adminRoot, realm, auth, rid, new ArrayList<>(rolesToAdd));
|
||||
|
||||
} else {
|
||||
Set<ObjectNode> rolesToAdd = getRoleRepresentations(roleNames, roleIds,
|
||||
new LocalSearch(RoleOperations.getRealmRolesAsNodes(adminRoot, realm, auth)));
|
||||
|
||||
// now remove the roles
|
||||
RoleOperations.removeRealmRoles(adminRoot, realm, auth, rid, new ArrayList<>(rolesToAdd));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("No user nor group, nor composite role specified. Use --uusername / --uid to specify user or --gname / --gid / --gpath to specify group or --rname / --rid to specify a composite role");
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
|
@ -257,6 +298,9 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
return uid != null || uusername != null;
|
||||
}
|
||||
|
||||
private boolean isCompositeRoleSpecified() {
|
||||
return rid != null || rname != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
|
@ -275,9 +319,10 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
StringWriter sb = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(sb);
|
||||
out.println("Usage: " + CMD + " remove-roles (--uusername USERNAME | --uid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println("Usage: " + CMD + " remove-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println(" " + CMD + " remove-roles (--gname NAME | --gpath PATH | --gid ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println(" " + CMD + " remove-roles (--rname ROLE_NAME | --rid ROLE_ID) [--cclientid CLIENT_ID | --cid ID] (--rolename NAME | --roleid ID)+ [ARGUMENTS]");
|
||||
out.println();
|
||||
out.println("Command to remove realm or client roles from a user or group.");
|
||||
out.println("Command to remove realm or client roles from a user, a group or a composite role.");
|
||||
out.println();
|
||||
out.println("Use `" + CMD + " config credentials` to establish an authenticated session, or use CREDENTIALS OPTIONS");
|
||||
out.println("to perform one time authentication.");
|
||||
|
@ -285,7 +330,8 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
out.println("If client is specified using --cclientid or --cid then roles to remove are client roles, otherwise they are realm roles.");
|
||||
out.println("Either a user, or a group needs to be specified. If user is specified using --uusername or --uid then roles are removed");
|
||||
out.println("from a specific user. If group is specified using --gname, --gpath or --gid then roles are removed from a specific group.");
|
||||
out.println("One or more roles have to be specified using --rolename or --roleid to be removed from a group or a user.");
|
||||
out.println("If composite role is specified using --rname or --rid then roles are removed from a specific composite role.");
|
||||
out.println("One or more roles have to be specified using --rolename or --roleid to be removed from a group, a user or a composite role.");
|
||||
out.println();
|
||||
out.println("Arguments:");
|
||||
out.println();
|
||||
|
@ -306,6 +352,8 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
|||
out.println(" to use --gid, or --gpath to specify the target group");
|
||||
out.println(" --gpath Group's 'path' attribute");
|
||||
out.println(" --gid Group's 'id' attribute");
|
||||
out.println(" --rname Composite role's 'name' attribute");
|
||||
out.println(" --rid Composite role's 'id' attribute");
|
||||
out.println(" --cclientid Client's 'clientId' attribute");
|
||||
out.println(" --cid Client's 'id' attribute");
|
||||
out.println(" --rolename Role's 'name' attribute");
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.io.StringWriter;
|
|||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.admin.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
|
@ -37,6 +38,9 @@ public class UpdateCmd extends AbstractRequestCmd {
|
|||
@Option(shortName = 'f', name = "file", description = "Read object from file or standard input if FILENAME is set to '-'")
|
||||
String file;
|
||||
|
||||
@Option(shortName = 'b', name = "body", description = "JSON object to be sent as-is or used as a template")
|
||||
String body;
|
||||
|
||||
@Option(shortName = 'F', name = "fields", description = "A pattern specifying which attributes of JSON response body to actually display as result - causes mismatch with Content-Length header")
|
||||
String fields;
|
||||
|
||||
|
@ -63,6 +67,7 @@ public class UpdateCmd extends AbstractRequestCmd {
|
|||
void initOptions() {
|
||||
// set options on parent
|
||||
super.file = file;
|
||||
super.body = body;
|
||||
super.fields = fields;
|
||||
super.printHeaders = printHeaders;
|
||||
super.returnId = false;
|
||||
|
@ -76,7 +81,7 @@ public class UpdateCmd extends AbstractRequestCmd {
|
|||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && file == null && (args == null || args.size() == 0);
|
||||
return noOptions() && file == null && body == null && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
|
@ -117,6 +122,7 @@ public class UpdateCmd extends AbstractRequestCmd {
|
|||
out.println(" NAME+=VALUE Add item VALUE to list attribute NAME");
|
||||
out.println(" -d, --delete NAME Remove a specific attribute NAME from JSON request body");
|
||||
out.println(" -f, --file FILENAME Read object from file or standard input if FILENAME is set to '-'");
|
||||
out.println(" -b, --body CONTENT Content to be sent as-is or used as a JSON object template");
|
||||
out.println(" -q, --query NAME=VALUE Add to request URI a NAME query parameter with value VALUE");
|
||||
out.println(" -h, --header NAME=VALUE Set request header NAME to VALUE");
|
||||
out.println(" -m, --merge Merge new values with existing configuration on the server");
|
||||
|
@ -150,7 +156,11 @@ public class UpdateCmd extends AbstractRequestCmd {
|
|||
out.println(" " + PROMPT + " " + CMD + " update realms/demorealm -s registrationAllowed=true");
|
||||
out.println();
|
||||
out.println("Update a client by overwriting existing configuration using local file as a template (replace ID with client's 'id'):");
|
||||
out.println(" " + PROMPT + " " + CMD + " update clients/ID -f new_my_client.json -s 'redirectUris=[\"http://localhost:8080/myapp/*\"]'");
|
||||
if (OS_ARCH.isWindows()) {
|
||||
out.println(" " + PROMPT + " " + CMD + " update clients/ID -f new_my_client.json -s \"redirectUris=[\\\"http://localhost:8080/myapp/*\\\"]\"");
|
||||
} else {
|
||||
out.println(" " + PROMPT + " " + CMD + " update clients/ID -f new_my_client.json -s 'redirectUris=[\"http://localhost:8080/myapp/*\"]'");
|
||||
}
|
||||
out.println();
|
||||
out.println("Update client by fetching current configuration from server and merging with specified changes (replace ID with client's 'id'):");
|
||||
out.println(" " + PROMPT + " " + CMD + " update clients/ID -f new_my_client.json -s enabled=true --merge");
|
||||
|
|
|
@ -23,8 +23,11 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doDeleteJSON;
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doGetJSON;
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doPostJSON;
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getAttrForType;
|
||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
|
@ -34,6 +37,28 @@ public class RoleOperations {
|
|||
public static class LIST_OF_ROLES extends ArrayList<RoleRepresentation>{};
|
||||
public static class LIST_OF_NODES extends ArrayList<ObjectNode>{};
|
||||
|
||||
public static String getIdFromRoleName(String adminRoot, String realm, String auth, String rname) {
|
||||
return getIdForType(adminRoot, realm, auth, "roles", "name", rname);
|
||||
}
|
||||
|
||||
public static void addRealmRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
||||
String resourceUrl = composeResourceUrl(rootUrl, realm, "roles-by-id/" + roleid + "/composites");
|
||||
doPostJSON(resourceUrl, auth, roles);
|
||||
}
|
||||
|
||||
public static void addClientRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
||||
addRealmRoles(rootUrl, realm, auth, roleid, roles);
|
||||
}
|
||||
|
||||
public static void removeRealmRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
||||
String resourceUrl = composeResourceUrl(rootUrl, realm, "roles-by-id/" + roleid + "/composites");
|
||||
doDeleteJSON(resourceUrl, auth, roles);
|
||||
}
|
||||
|
||||
public static void removeClientRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
||||
removeRealmRoles(rootUrl, realm, auth, roleid, roles);
|
||||
}
|
||||
|
||||
public static String getRoleNameFromId(String adminRoot, String realm, String auth, String rid) {
|
||||
return getAttrForType(adminRoot, realm, auth, "roles", "id", rid, "name");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
package org.keycloak.testsuite.cli.admin;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.client.admin.cli.config.FileConfigHandler;
|
||||
import org.keycloak.testsuite.cli.KcAdmExec;
|
||||
import org.keycloak.testsuite.util.TempFileResource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||
import static org.keycloak.testsuite.cli.KcAdmExec.execute;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class KcAdmSessionTest extends AbstractAdmCliTest {
|
||||
|
||||
static Class<? extends List<ObjectNode>> LIST_OF_JSON = new ArrayList<ObjectNode>() {}.getClass();
|
||||
|
||||
@Test
|
||||
public void test() throws IOException {
|
||||
|
||||
FileConfigHandler handler = initCustomConfigFile();
|
||||
|
||||
try (TempFileResource configFile = new TempFileResource(handler.getConfigFile())) {
|
||||
|
||||
// login as admin
|
||||
loginAsUser(configFile.getFile(), serverUrl, "master", "admin", "admin");
|
||||
|
||||
// create realm
|
||||
KcAdmExec exe = execute("create realms --config '" + configFile.getName() + "' -s realm=demorealm -s enabled=true");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||
Assert.assertTrue(exe.stderrLines().get(0).startsWith("Created "));
|
||||
|
||||
// create user
|
||||
exe = execute("create users --config '" + configFile.getName() + "' -r demorealm -s username=testuser -s enabled=true -i");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 1, 0);
|
||||
String userId = exe.stdoutLines().get(0);
|
||||
|
||||
// add realm admin capabilities to user
|
||||
exe = execute("add-roles --config '" + configFile.getName() + "' -r demorealm --uusername testuser --cclientid realm-management --rolename realm-admin");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
// set password for the user
|
||||
exe = execute("set-password --config '" + configFile.getName() + "' -r demorealm --username testuser -p password");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
|
||||
|
||||
// login as testuser
|
||||
loginAsUser(configFile.getFile(), serverUrl, "demorealm", "testuser", "password");
|
||||
|
||||
|
||||
// get realm roles
|
||||
exe = execute("get-roles --config '" + configFile.getName() + "'");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
List<ObjectNode> roles = loadJson(exe.stdout(), LIST_OF_JSON);
|
||||
Assert.assertTrue("expect two realm roles available", roles.size() == 2);
|
||||
|
||||
// create realm role
|
||||
exe = execute("create roles --config '" + configFile.getName() + "' -s name=testrole -s 'description=Test role' -o");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
ObjectNode role = loadJson(exe.stdout(), ObjectNode.class);
|
||||
Assert.assertEquals("testrole", role.get("name").asText());
|
||||
String roleId = role.get("id").asText();
|
||||
|
||||
// get realm roles again
|
||||
exe = execute("get-roles --config '" + configFile.getName() + "'");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
roles = loadJson(exe.stdout(), LIST_OF_JSON);
|
||||
Assert.assertTrue("expect three realm roles available", roles.size() == 3);
|
||||
|
||||
// create client
|
||||
exe = execute("create clients --config '" + configFile.getName() + "' -s clientId=testclient -i");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 1, 0);
|
||||
String idOfClient = exe.stdoutLines().get(0);
|
||||
|
||||
|
||||
// create client role
|
||||
exe = execute("create clients/" + idOfClient + "/roles --config '" + configFile.getName() + "' -s name=clientrole -s 'description=Test client role'");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||
Assert.assertTrue(exe.stderrLines().get(0).startsWith("Created "));
|
||||
|
||||
// make sure client role has been created
|
||||
exe = execute("get-roles --config '" + configFile.getName() + "' --cclientid testclient");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
roles = loadJson(exe.stdout(), LIST_OF_JSON);
|
||||
Assert.assertTrue("expect one role", roles.size() == 1);
|
||||
Assert.assertEquals("clientrole", roles.get(0).get("name").asText());
|
||||
|
||||
// add created role to user - we are realm admin so we can add role to ourself
|
||||
exe = execute("add-roles --config '" + configFile.getName() + "' --uusername testuser --cclientid testclient --rolename clientrole");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
|
||||
// make sure the roles have been added
|
||||
exe = execute("get-roles --config '" + configFile.getName() + "' --uusername testuser --all");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
ObjectNode node = loadJson(exe.stdout(), ObjectNode.class);
|
||||
Assert.assertNotNull(node.get("realmMappings"));
|
||||
|
||||
List<String> realmMappings = StreamSupport.stream(node.get("realmMappings").spliterator(), false)
|
||||
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("offline_access", "uma_authorization"), realmMappings);
|
||||
|
||||
ObjectNode clientRoles = (ObjectNode) node.get("clientMappings");
|
||||
//List<String> fields = asSortedList(clientRoles.fieldNames());
|
||||
List<String> fields = StreamSupport.stream(clientRoles.spliterator(), false)
|
||||
.map(o -> o.get("client").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("account", "realm-management", "testclient"), fields);
|
||||
|
||||
realmMappings = StreamSupport.stream(clientRoles.get("account").get("mappings").spliterator(), false)
|
||||
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("manage-account", "view-profile"), realmMappings);
|
||||
|
||||
realmMappings = StreamSupport.stream(clientRoles.get("realm-management").get("mappings").spliterator(), false)
|
||||
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("realm-admin"), realmMappings);
|
||||
|
||||
realmMappings = StreamSupport.stream(clientRoles.get("testclient").get("mappings").spliterator(), false)
|
||||
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("clientrole"), realmMappings);
|
||||
|
||||
|
||||
|
||||
// add a realm role to the user
|
||||
exe = execute("add-roles --config '" + configFile.getName() + "' --uusername testuser --rolename testrole");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
|
||||
// get all roles for the user again
|
||||
exe = execute("get-roles --config '" + configFile.getName() + "' --uusername testuser --all");
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
|
||||
node = loadJson(exe.stdout(), ObjectNode.class);
|
||||
Assert.assertNotNull(node.get("realmMappings"));
|
||||
|
||||
realmMappings = StreamSupport.stream(node.get("realmMappings").spliterator(), false)
|
||||
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
|
||||
Assert.assertEquals(Arrays.asList("offline_access", "testrole", "uma_authorization"), realmMappings);
|
||||
|
||||
// create a group
|
||||
exe = execute("create groups --config '" + configFile.getName() + "' -s name=TestUsers -i");
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
String groupId = exe.stdoutLines().get(0);
|
||||
|
||||
// create a sub-group
|
||||
exe = execute("create groups/" + groupId + "/children --config '" + configFile.getName() + "' -s name=TestPowerUsers -i");
|
||||
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||
String subGroupId = exe.stdoutLines().get(0);
|
||||
|
||||
// add testuser to TestPowerUsers
|
||||
exe = execute("update users/" + userId + "/groups/" + subGroupId + " --config '" + configFile.getName()
|
||||
+ "' -s realm=demorealm -s userId=" + userId + " -s groupId=" + subGroupId + " -n");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
// delete everything
|
||||
exe = execute("delete groups/" + subGroupId + " --config '" + configFile.getName() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
exe = execute("delete groups/" + groupId + " --config '" + configFile.getName() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
exe = execute("delete clients/" + idOfClient + " --config '" + configFile.getName() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
exe = execute("delete roles/testrole --config '" + configFile.getName() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
exe = execute("delete users/" + userId + " --config '" + configFile.getName() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
// delete realm as well - using initial master realm session still saved in config file
|
||||
exe = execute("delete realms/demorealm --config '" + configFile.getName() + "' --realm master");
|
||||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue