All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.cli.UsersCli Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.cli;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.rocksdb.RocksDB;
import org.yamcs.http.api.IamApi;
import org.yamcs.protobuf.Mdb.SignificanceInfo.SignificanceLevelType;
import org.yamcs.protobuf.UserInfo;
import org.yamcs.security.Directory;
import org.yamcs.security.User;
import org.yamcs.security.protobuf.Clearance;
import org.yamcs.utils.TimeEncoding;

import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.google.protobuf.util.JsonFormat;

/**
 * Generates password hashes for use in users.yaml
 */
@Parameters(commandDescription = "User operations")
public class UsersCli extends Command {

    public UsersCli(YamcsAdminCli yamcsCli) {
        super("users", yamcsCli);
        addSubCommand(new AddRole());
        addSubCommand(new CheckPassword());
        addSubCommand(new CreateUser());
        addSubCommand(new DeleteUser());
        addSubCommand(new DescribeUser());
        addSubCommand(new ListUsers());
        addSubCommand(new RemoveIdentity());
        addSubCommand(new RemoveRole());
        addSubCommand(new ResetPassword());
        addSubCommand(new UpdateUser());
        TimeEncoding.setUp();
    }

    @Parameters(commandDescription = "Add a role to a user")
    private class AddRole extends Command {

        @Parameter(description = "The name of the user.")
        private List username;

        @Parameter(names = "--role", required = true, description = "Role to be added.")
        private String role;

        AddRole() {
            super("add-role", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }

            user.addRole(role, false);
            directory.updateUserProperties(user);
        }
    }

    @Parameters(commandDescription = "Remove an identity from a user")
    private class RemoveIdentity extends Command {

        @Parameter(description = "The name of the user.")
        private List username;

        @Parameter(names = "--identity", required = true, description = "Identity to be removed.")
        private String identity;

        RemoveIdentity() {
            super("remove-identity", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }

            user.deleteIdentity(identity);
            directory.updateUserProperties(user);
        }
    }

    @Parameters(commandDescription = "Remove a role from a user")
    private class RemoveRole extends Command {

        @Parameter(description = "The name of the user.")
        private List username;

        @Parameter(names = "--role", required = true, description = "Role to be removed.")
        private String role;

        RemoveRole() {
            super("remove-role", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }

            user.deleteRole(role);
            directory.updateUserProperties(user);
        }
    }

    @Parameters(commandDescription = "Update a user")
    private class UpdateUser extends Command {

        @Parameter(description = "The name of the user.")
        private List username;

        @Parameter(names = "--display-name", description = "Displayed name of the user.")
        private String displayName;

        @Parameter(names = "--email", description = "User email.")
        private String email;

        @Parameter(names = "--active", arity = 1, description = "Activate this user.")
        private Boolean active;

        @Parameter(names = "--superuser", arity = 1, description = "Grant superuser privileges")
        private Boolean superuser;

        @Parameter(names = "--clearance", description = "Clearance level of the user", converter = SignificanceLevelConverter.class)
        private SignificanceLevelType clearance;

        UpdateUser() {
            super("update", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }

            if (displayName != null) {
                user.setDisplayName(displayName);
            }
            if (email != null) {
                user.setEmail(email);
            }
            if (active != null) {
                user.setActive(active);
            }
            if (superuser != null) {
                user.setSuperuser(superuser);
            }
            if (clearance != null) {
                user.setClearance(Clearance.newBuilder()
                        .setLevel(clearance.name())
                        .setIssueTime(TimeEncoding.toProtobufTimestamp(TimeEncoding.getWallclockTime()))
                        .build());
            }

            directory.updateUserProperties(user);
        }
    }

    @Parameters(commandDescription = "Create a new user")
    private class CreateUser extends Command {

        @Parameter(description = "The name of the new user.")
        private List username;

        @Parameter(names = "--email", description = "User email.")
        private String email;

        @Parameter(names = "--display-name", description = "Displayed name of the user.")
        private String displayName;

        @Parameter(names = "--inactive", description = "Add this flag to prevent Yamcs from activating the user.")
        private boolean inactive;

        @Parameter(names = "--superuser", description = "Add this flag to grant the user superuser privileges.")
        private boolean superuser;

        @Parameter(names = "--no-password", description = "Add this flag to indicate that this user should not have a password. This will also bypass the password prompt.")
        private boolean noPassword;

        @Parameter(names = "--clearance", description = "Clearance level of the user", converter = SignificanceLevelConverter.class)
        private SignificanceLevelType clearance;

        CreateUser() {
            super("create", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user != null) {
                console.println("user already exists: '" + username.get(0) + "'");
                exit(-1);
            }

            user = new User(username.get(0), null);
            user.setDisplayName(displayName);
            user.setEmail(email);
            user.setSuperuser(superuser);

            if (clearance != null) {
                user.setClearance(Clearance.newBuilder()
                        .setLevel(clearance.name())
                        .setIssueTime(TimeEncoding.toProtobufTimestamp(TimeEncoding.getWallclockTime()))
                        .build());
            }

            char[] password = null;
            if (!noPassword) {
                String passwordString = System.getenv("YAMCSADMIN_PASSWORD");
                if (passwordString == null) {
                    console.println("Enter password: ");
                    password = console.readPassword(false);
                    console.println("Confirm password: ");
                    char[] confirmedPassword = console.readPassword(false);

                    if (!Arrays.equals(password, confirmedPassword)) {
                        console.println("Password confirmation does not match\n");
                        exit(-1);
                    }
                } else {
                    password = passwordString.trim().toCharArray();
                }
            }

            if (!inactive) {
                user.confirm();
            }

            directory.addUser(user);
            if (password != null) {
                directory.changePassword(user, password);
            }
        }
    }

    @Parameters(commandDescription = "List users")
    private class ListUsers extends Command {

        ListUsers() {
            super("list", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            switch (getFormat()) {
            case JSON:
                List users = directory.getUsers().stream()
                        .map(user -> IamApi.toUserInfo(user, true, directory))
                        .collect(Collectors.toList());
                console.println(printJsonArray(users));
                break;
            default:
                TableStringBuilder b = new TableStringBuilder("username", "display name", "email", "active",
                        "superuser");
                directory.getUsers().forEach(user -> {
                    b.addLine(user.getName(), user.getDisplayName(), user.getEmail(), user.isActive(),
                            user.isSuperuser());
                });
                console.println(b.toString());
            }
        }
    }

    @Parameters(commandDescription = "Describe user details")
    private class DescribeUser extends Command {

        @Parameter()
        private List username;

        DescribeUser() {
            super("describe", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username + "'");
                exit(-1);
            }

            switch (getFormat()) {
            case JSON:
                UserInfo userinfo = IamApi.toUserInfo(user, true, directory);
                console.println(JsonFormat.printer().print(userinfo));
                break;
            default:
                TableStringBuilder b = new TableStringBuilder(2);
                b.addLine("id:", user.getId());
                b.addLine("username:", user.getName());
                b.addLine("display name:", user.getDisplayName());
                b.addLine("email:", user.getEmail());
                b.addLine("active:", user.isActive());
                b.addLine("superuser:", user.isSuperuser());
                b.addLine("roles:", String.join(", ", user.getRoles()));
                if (user.getClearance() != null) {
                    b.addLine("clearance:", user.getClearance().getLevel());
                }
                b.addLine("external:", user.isExternallyManaged());
                b.addLine("created:", printInstant(user.getCreationTime()));
                b.addLine("confirmed:", printInstant(user.getConfirmationTime()));
                b.addLine("last login:", printInstant(user.getLastLoginTime()));
                console.println(b.toString());
            }
        }

        private String printInstant(long instant) {
            if (instant == TimeEncoding.INVALID_INSTANT) {
                return "";
            } else {
                return TimeEncoding.toString(instant);
            }

        }
    }

    @Parameters(commandDescription = "Delete user")
    private class DeleteUser extends Command {

        @Parameter()
        private List username;

        DeleteUser() {
            super("delete", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username + "'");
                exit(-1);
            }

            directory.deleteUser(user);
        }
    }

    @Parameters(commandDescription = "Reset a user's password")
    private class ResetPassword extends Command {

        @Parameter()
        private List username;

        ResetPassword() {
            super("reset-password", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }
            if (user.isExternallyManaged()) {
                console.println("credentials of user '" + username.get(0) + "' are not managed by Yamcs");
                exit(-1);
            }

            char[] newPassword;
            String newPasswordString = System.getenv("YAMCSADMIN_PASSWORD");
            if (newPasswordString == null) {
                console.print("Enter new password: ");
                newPassword = console.readPassword(false);
                console.print("Confirm new password: ");
                char[] confirmedPassword = console.readPassword(false);

                if (!Arrays.equals(newPassword, confirmedPassword)) {
                    console.println("Password confirmation does not match\n");
                    exit(-1);
                }
            } else {
                newPassword = newPasswordString.trim().toCharArray();
            }

            directory.changePassword(user, newPassword);
            console.println("Password updated successfully");
        }
    }

    @Parameters(commandDescription = "Check a user's password")
    private class CheckPassword extends Command {

        @Parameter()
        private List username;

        CheckPassword() {
            super("check-password", UsersCli.this);
        }

        @Override
        void execute() throws Exception {
            RocksDB.loadLibrary();
            Directory directory = new Directory();

            if (username == null) {
                console.println("username not specified");
                exit(-1);
            }

            User user = directory.getUser(username.get(0));
            if (user == null) {
                console.println("invalid user '" + username.get(0) + "'");
                exit(-1);
            }
            if (user.isExternallyManaged()) {
                console.println("credentials of user '" + username.get(0) + "' are not managed by Yamcs");
                exit(-1);
            }

            char[] password;
            String passwordString = System.getenv("YAMCSADMIN_PASSWORD");
            if (passwordString == null) {
                console.print("Enter password: ");
                password = console.readPassword(false);
            } else {
                password = passwordString.trim().toCharArray();
            }

            if (directory.validateUserPassword(user.getName(), password)) {
                console.println("Password correct");
            } else {
                console.println("Password incorrect");
                exit(-1);
            }
        }
    }

    // Keep public, required by JCommander
    public static class SignificanceLevelConverter implements IStringConverter {

        @Override
        public SignificanceLevelType convert(String value) {
            try {
                return SignificanceLevelType.valueOf(value.toUpperCase());
            } catch (IllegalArgumentException e) {
                throw new ParameterException(
                        "Unknown value for --clearance. Possible values: "
                                + Arrays.asList(SignificanceLevelType.values()));
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy