/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserCommands {
public static class Create extends AbstractCommand {
private String usernamePrefix;
private String password;
private String realmName;
private String roleNames;
@Override
public String getName() {
return "createUsers";
}
private class StateHolder {
int firstInThisBatch;
int countInThisBatch;
int remaining;
};
@Override
protected void doRunCommand(KeycloakSession session) {
usernamePrefix = getArg(0);
password = getArg(1);
realmName = getArg(2);
int first = getIntArg(3);
int count = getIntArg(4);
int batchCount = getIntArg(5);
roleNames = getArg(6);
final StateHolder state = new StateHolder();
state.firstInThisBatch = first;
state.remaining = count;
state.countInThisBatch = Math.min(batchCount, state.remaining);
while (state.remaining > 0) {
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
createUsersInBatch(session, state.firstInThisBatch, state.countInThisBatch);
}
});
// update state
state.firstInThisBatch = state.firstInThisBatch + state.countInThisBatch;
state.remaining = state.remaining - state.countInThisBatch;
state.countInThisBatch = Math.min(batchCount, state.remaining);
}
log.infof("Command finished. All users from %s to %s created", usernamePrefix + first, usernamePrefix + (first + count - 1));
}
private void createUsersInBatch(KeycloakSession session, int first, int count) {
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
throw new HandledException();
}
Set<RoleModel> roles = findRoles(realm, roleNames);
int last = first + count;
for (int counter = first; counter < last; counter++) {
String username = usernamePrefix + counter;
UserModel user = session.users().addUser(realm, username);
user.setEnabled(true);
user.setEmail(username + "@keycloak.org");
UserCredentialModel passwordCred = UserCredentialModel.password(password);
session.userCredentialManager().updateCredential(realm, user, passwordCred);
for (RoleModel role : roles) {
user.grantRole(role);
}
}
log.infof("Users from %s to %s created", usernamePrefix + first, usernamePrefix + (last - 1));
}
@Override
public String printUsage() {
return super.printUsage() + " <username-prefix> <password> <realm-name> <starting-user-offset> <total-count> <batch-size> <realm-roles-list>. " +
"\n'total-count' refers to total count of newly created users. 'batch-size' refers to number of created users in each transaction. 'starting-user-offset' refers to starting username offset." +
"\nFor example if 'starting-user-offset' is 15 and total-count is 10 and username-prefix is 'test', it will create users test15, test16, test17, ... , test24" +
"\nRoles list is divided by comma (client roles are referenced with format <client-id>/<role-name> )>\n" +
"Example usage: " + super.printUsage() + " test test demo 0 500 100 user,admin";
}
private Set<RoleModel> findRoles(RealmModel realm, String rolesList) {
Set<RoleModel> result = new HashSet<>();
String[] roles = rolesList.split(",");
for (String roleName : roles) {
roleName = roleName.trim();
RoleModel role;
if (roleName.contains("/")) {
String[] spl = roleName.split("/");
ClientModel client = realm.getClientByClientId(spl[0]);
if (client == null) {
log.errorf("Client not found: %s", spl[0]);
throw new HandledException();
}
role = client.getRole(spl[1]);
} else {
role = realm.getRole(roleName);
}
if (role == null) {
log.errorf("Role not found: %s", roleName);
throw new HandledException();
}
result.add(role);
}
return result;
}
}
public static class Remove extends AbstractCommand {
@Override
public String getName() {
return "removeUsers";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String usernamePrefix = getArg(0);
String realmName = getArg(1);
int first = getIntArg(2);
int count = getIntArg(3);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
int last = first + count;
for (int counter = first; counter < last; counter++) {
String username = usernamePrefix + counter;
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
log.errorf("User '%s' not found", username);
} else {
session.users().removeUser(realm, user);
}
}
log.infof("Users from %s to %s removed", usernamePrefix + first, usernamePrefix + (last - 1));
}
@Override
public String printUsage() {
return super.printUsage() + " <username-prefix> <realm-name> <starting-user-offset> <count> \n" +
"Example usage: " + super.printUsage() + " test demo 0 20";
}
}
public static class Count extends AbstractCommand {
@Override
public String getName() {
return "getUsersCount";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String realmName = getArg(0);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
int usersCount = session.users().getUsersCount(realm);
log.infof("Users count in realm %s: %d", realmName, usersCount);
}
@Override
public String printUsage() {
return super.printUsage() + " <realm-name>";
}
}
public static class GetUser extends AbstractCommand {
@Override
public String getName() {
return "getUser";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String realmName = getArg(0);
String username = getArg(1);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
log.infof("User '%s' doesn't exist in realm '%s'", username, realmName);
} else {
List<String> roleMappings = getRoleMappings(session, realm, user);
log.infof("User: ID: '%s', username: '%s', mail: '%s', roles: '%s'", user.getId(), user.getUsername(), user.getEmail(), roleMappings.toString());
}
}
private List<String> getRoleMappings(KeycloakSession session, RealmModel realm, UserModel user) {
Set<RoleModel> roles = user.getRoleMappings();
List<String> result = new LinkedList<>();
for (RoleModel role : roles) {
if (role.getContainer() instanceof RealmModel) {
result.add(role.getName());
} else {
ClientModel client = (ClientModel) role.getContainer();
result.add(client.getClientId() + "/" + role.getName());
}
}
return result;
}
@Override
public String printUsage() {
return super.printUsage() + " <realm-name> <username>";
}
}
}