@Override
    public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException {
        if(log.isDebugEnabled()) {
            log.debug(String.format("Validate login credentials for %s", bookmark));
        }
        final Credentials credentials = bookmark.getCredentials();
        if(credentials.isPublicKeyAuthentication()) {
            if(!credentials.getIdentity().attributes().getPermission().isReadable()) {
                log.warn(String.format("Prompt to select identity file not readable %s", credentials.getIdentity()));
                credentials.setIdentity(prompt.select(credentials.getIdentity()));
            }
        }
        if(options.keychain) {
            if(options.password) {
                if(StringUtils.isBlank(credentials.getPassword())) {
                    final String password = keychain.findLoginPassword(bookmark);
                    if(StringUtils.isNotBlank(password)) {
                        if(log.isInfoEnabled()) {
                            log.info(String.format("Fetched password from keychain for %s", bookmark));
                        }
                        // No need to reinsert found password to the keychain.
                        credentials.setSaved(false);
                        credentials.setPassword(password);
                    }
                }
            }
            if(options.token) {
                if(StringUtils.isBlank(credentials.getToken())) {
                    final String token = keychain.findLoginToken(bookmark);
                    if(StringUtils.isNotBlank(token)) {
                        if(log.isInfoEnabled()) {
                            log.info(String.format("Fetched token from keychain for %s", bookmark));
                        }
                        // No need to reinsert found token to the keychain.
                        credentials.setSaved(false);
                        credentials.setToken(token);
                    }
                }
            }
            if(options.publickey) {
                final String passphrase = keychain.findPrivateKeyPassphrase(bookmark);
                if(StringUtils.isNotBlank(passphrase)) {
                    if(log.isInfoEnabled()) {
                        log.info(String.format("Fetched private key passphrase from keychain for %s", bookmark));
                    }
                    // No need to reinsert found token to the keychain.
                    credentials.setSaved(false);
                    credentials.setIdentityPassphrase(passphrase);
                }
            }
            if(options.oauth) {
                final OAuthTokens tokens = keychain.findOAuthTokens(bookmark);
                if(tokens.validate()) {
                    if(log.isInfoEnabled()) {
                        log.info(String.format("Fetched OAuth token from keychain for %s", bookmark));
                    }
                    // No need to reinsert found token to the keychain.
                    credentials.setSaved(tokens.isExpired());
                    credentials.setOauth(tokens);
                }
            }
        }
        if(!credentials.validate(bookmark.getProtocol(), options)) {
            final CredentialsConfigurator configurator = bookmark.getProtocol().getFeature(CredentialsConfigurator.class);
            if(log.isDebugEnabled()) {
                log.debug(String.format("Auto configure credentials with %s", configurator));
            }
            bookmark.setCredentials(configurator.configure(bookmark));
        }
        if(!credentials.validate(bookmark.getProtocol(), options)) {
            final StringAppender message = new StringAppender();
            if(options.password) {
                message.append(MessageFormat.format(LocaleFactory.localizedString(
                        "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(bookmark)));
            }
            if(options.publickey) {
                message.append(LocaleFactory.localizedString(
                        "Select the private key in PEM or PuTTY format", "Credentials"));
            }
            message.append(LocaleFactory.localizedString("No login credentials could be found in the Keychain", "Credentials"));
            this.prompt(bookmark, message.toString(), prompt, options);
        }
    }
@Test
    public void testFindPasswordSftp() throws Exception {
        final AtomicBoolean keychain = new AtomicBoolean(false);
        KeychainLoginService l = new KeychainLoginService(new DisabledPasswordStore() {
            @Override
            public String findLoginPassword(final Host bookmark) {
                keychain.set(true);
                return "P";
            }
        }
        );
        final Credentials credentials = new Credentials();
        credentials.setUsername("u");
        final Host host = new Host(new TestProtocol(), "test.cyberduck.ch", credentials);
        l.validate(host, new DisabledLoginCallback(), new LoginOptions(host.getProtocol()));
        assertTrue(keychain.get());
        assertFalse(host.getCredentials().isSaved());
        assertEquals("P", host.getCredentials().getPassword());
    }