public static CreateUserStatements get()

in locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/CreateUserStatements.java [120:302]


    public static CreateUserStatements get(JcloudsLocation location, @Nullable Image image, ConfigBag config) {
        //NB: private key is not installed remotely, just used to get/validate the public key
        Preconditions.checkNotNull(location, "location argument required");
        String user = Preconditions.checkNotNull(location.getUser(config), "user required");
        final boolean isWindows = location.isWindows(image, config);
        final String explicitLoginUser = config.get(JcloudsLocation.LOGIN_USER);
        final String loginUser = groovyTruth(explicitLoginUser)
                ? explicitLoginUser
                : (image != null && image.getDefaultCredentials() != null)
                        ? image.getDefaultCredentials().identity
                        : null;
        final boolean dontCreateUser = config.get(JcloudsLocation.DONT_CREATE_USER);
        final boolean grantUserSudo = config.get(JcloudsLocation.GRANT_USER_SUDO);
        final LocationConfigUtils.OsCredential credential = LocationConfigUtils.getOsCredential(config, ResourceUtils.create(location));
        credential.checkNoErrors().logAnyWarnings();
        final String passwordToSet =
                Strings.isNonBlank(credential.getPassword()) ? credential.getPassword() : Identifiers.makeRandomId(12);
        final List<Statement> statements = Lists.newArrayList();
        LoginCredentials createdUserCreds = null;

        if (dontCreateUser) {
            // dontCreateUser:
            // if caller has not specified a user, we'll just continue to use the loginUser;
            // if caller *has*, we set up our credentials assuming that user and credentials already exist

            if (Strings.isBlank(user)) {
                // createdUserCreds returned from this method will be null;
                // we will use the creds returned by jclouds on the node
                LOG.info("Not setting up user {} (subsequently using loginUser {})", user, loginUser);
                config.put(JcloudsLocation.USER, loginUser);

            } else {
                LOG.info("Not creating user {}, and not installing its password or authorizing keys (assuming it exists)", user);

                if (credential.isUsingPassword()) {
                    createdUserCreds = LoginCredentials.builder().user(user).password(credential.getPassword()).build();
                    if (Boolean.FALSE.equals(config.get(JcloudsLocation.DISABLE_ROOT_AND_PASSWORD_SSH))) {
                        statements.add(SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
                    }
                } else if (credential.hasKey()) {
                    createdUserCreds = LoginCredentials.builder().user(user).privateKey(credential.getPrivateKeyData()).build();
                }
            }

        } else if (isWindows) {
            // TODO Generate statements to create the user.
            // createdUserCreds returned from this method will be null;
            // we will use the creds returned by jclouds on the node
            LOG.warn("Not creating or configuring user on Windows VM, despite " + JcloudsLocation.DONT_CREATE_USER.getName() + " set to false");

            // TODO extractVmCredentials() will use user:publicKeyData defaults, if we don't override this.
            // For linux, how would we configure Brooklyn to use the node.getCredentials() - i.e. the version
            // that the cloud automatically generated?
            if (config.get(JcloudsLocation.USER) != null) config.put(JcloudsLocation.USER, "");
            if (config.get(JcloudsLocation.PASSWORD) != null) config.put(JcloudsLocation.PASSWORD, "");
            if (config.get(JcloudsLocation.PRIVATE_KEY_DATA) != null) config.put(JcloudsLocation.PRIVATE_KEY_DATA, "");
            if (config.get(JcloudsLocation.PRIVATE_KEY_FILE) != null) config.put(JcloudsLocation.PRIVATE_KEY_FILE, "");
            if (config.get(JcloudsLocation.PUBLIC_KEY_DATA) != null) config.put(JcloudsLocation.PUBLIC_KEY_DATA, "");
            if (config.get(JcloudsLocation.PUBLIC_KEY_FILE) != null) config.put(JcloudsLocation.PUBLIC_KEY_FILE, "");

        } else if (Strings.isBlank(user) || user.equals(loginUser) || user.equals(JcloudsLocation.ROOT_USERNAME)) {
            boolean useKey = Strings.isNonBlank(credential.getPublicKeyData());

            // For subsequent ssh'ing, we'll be using the loginUser
            if (Strings.isBlank(user)) {
                user = loginUser;
                config.put(JcloudsLocation.USER, user);
            }

            // Using the pre-existing loginUser; setup the publicKey/password so can login as expected

            // *Always* change the password (unless dontCreateUser was specified)
            statements.add(new ReplaceShadowPasswordEntry(Sha512Crypt.function(), user, passwordToSet));
            createdUserCreds = LoginCredentials.builder().user(user).password(passwordToSet).build();

            if (useKey) {
                // NB: further keys are added from config *after* user creation
                statements.add(new AuthorizeRSAPublicKeys("~" + user + "/.ssh", ImmutableList.of(credential.getPublicKeyData()), null));
                if (Strings.isNonBlank(credential.getPrivateKeyData())) {
                    createdUserCreds = LoginCredentials.builder().user(user).privateKey(credential.getPrivateKeyData()).build();
                }
            }

            if (!useKey || Boolean.FALSE.equals(config.get(JcloudsLocation.DISABLE_ROOT_AND_PASSWORD_SSH))) {
                // ensure password is permitted for ssh
                statements.add(SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
                if (user.equals(JcloudsLocation.ROOT_USERNAME)) {
                    statements.add(SshStatements.sshdConfig(ImmutableMap.of("PermitRootLogin", "yes")));
                }
            }

        } else {
            String pubKey = credential.getPublicKeyData();
            String privKey = credential.getPrivateKeyData();

            if (credential.isEmpty()) {
                /*
                 * TODO have an explicit `create_new_key_per_machine` config key.
                 * error if privateKeyData is set in this case.
                 * publicKeyData automatically added to EXTRA_SSH_KEY_URLS_TO_AUTH.
                 *
                 * if this config key is not set, use a key `brooklyn_id_rsa` and `.pub` in `MGMT_BASE_DIR`,
                 * with permission 0600, creating it if necessary, and logging the fact that this was created.
                 */
                // TODO JcloudsLocation used to log this once only: loggedSshKeysHint.compareAndSet(false, true).
                if (!config.containsKey(JcloudsLocation.PRIVATE_KEY_FILE)) {
                    LOG.info("Default SSH keys not found or not usable; will create new keys for each machine. " +
                                    "Create ~/.ssh/id_rsa or set {} / {} / {} as appropriate for this location " +
                                    "if you wish to be able to log in without Brooklyn.",
                            new Object[]{JcloudsLocation.PRIVATE_KEY_FILE.getName(), JcloudsLocation.PRIVATE_KEY_PASSPHRASE.getName(), JcloudsLocation.PASSWORD.getName()});
                }
                KeyPair newKeyPair = SecureKeys.newKeyPair();
                pubKey = SecureKeys.toPub(newKeyPair);
                privKey = SecureKeys.toPem(newKeyPair);
                LOG.debug("Brooklyn key being created for " + user + " at new machine " + location + " is:\n" + privKey);
            }

            // Create the user
            // note AdminAccess requires _all_ fields set, due to http://code.google.com/p/jclouds/issues/detail?id=1095
            AdminAccess.Builder adminBuilder = AdminAccess.builder()
                    .adminUsername(user)
                    .grantSudoToAdminUser(grantUserSudo);
            adminBuilder.cryptFunction(Sha512Crypt.function());

            boolean useKey = Strings.isNonBlank(pubKey);
            adminBuilder.cryptFunction(Sha512Crypt.function());

            // always set this password; if not supplied, it will be a random string
            adminBuilder.adminPassword(passwordToSet);
            // log the password also, in case we need it
            LOG.debug("Password '{}' being created for user '{}' at the machine we are about to provision in {}; {}",
                    new Object[]{passwordToSet, user, location, useKey ? "however a key will be used to access it" : "this will be the only way to log in"});

            if (grantUserSudo && config.get(JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH)) {
                // the default - set root password which we forget, because we have sudo acct
                // (and lock out root and passwords from ssh)
                adminBuilder.resetLoginPassword(true);
                adminBuilder.loginPassword(Identifiers.makeRandomId(12));
            } else {
                adminBuilder.resetLoginPassword(false);
                adminBuilder.loginPassword(Identifiers.makeRandomId(12) + "-ignored");
            }

            if (useKey) {
                adminBuilder.authorizeAdminPublicKey(true).adminPublicKey(pubKey);
            } else {
                adminBuilder.authorizeAdminPublicKey(false).adminPublicKey(Identifiers.makeRandomId(12) + "-ignored");
            }

            // jclouds wants us to give it the private key, otherwise it might refuse to authorize the public key
            // (in AdminAccess.build, if adminUsername != null && adminPassword != null);
            // we don't want to give it the private key, but we *do* want the public key authorized;
            // this code seems to trigger that.
            // (we build the creds below)
            adminBuilder.installAdminPrivateKey(false).adminPrivateKey(Identifiers.makeRandomId(12) + "-ignored");

            // lock SSH means no root login and no passwordless login
            // if we're using a password or we don't have sudo, then don't do this!
            adminBuilder.lockSsh(useKey && grantUserSudo && config.get(JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH));

            statements.add(adminBuilder.build());

            if (useKey) {
                createdUserCreds = LoginCredentials.builder().user(user).privateKey(privKey).build();
            } else if (passwordToSet != null) {
                createdUserCreds = LoginCredentials.builder().user(user).password(passwordToSet).build();
            }

            if (!useKey || Boolean.FALSE.equals(config.get(JcloudsLocation.DISABLE_ROOT_AND_PASSWORD_SSH))) {
                // ensure password is permitted for ssh
                statements.add(SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
            }
        }

        String customTemplateOptionsScript = config.get(JcloudsLocation.CUSTOM_TEMPLATE_OPTIONS_SCRIPT_CONTENTS);
        if (Strings.isNonBlank(customTemplateOptionsScript)) {
            statements.add(new LiteralStatement(customTemplateOptionsScript));
        }

        LOG.debug("Machine we are about to create in {} will be customized with: {}", location, statements);

        return new CreateUserStatements(createdUserCreds, statements);
    }