// Copyright (C) 2016 The Android Open Source Project // // 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 com.google.gerrit.server.account; import static com.google.common.truth.Truth.assertThat; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountSshKey; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.junit.Test; public class AuthorizedKeysTest { private static final String KEY1 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCgug5VyMXQGnem2H1KVC4/HcRcD4zzBqS" + "uJBRWVonSSoz3RoAZ7bWXCVVGwchtXwUURD689wFYdiPecOrWOUgeeyRq754YWRhU+W28" + "vf8IZixgjCmiBhaL2gt3wff6pP+NXJpTSA4aeWE5DfNK5tZlxlSxqkKOS8JRSUeNQov5T" + "w== john.doe@example.com"; private static final String KEY2 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDm5yP7FmEoqzQRDyskX+9+N0q9GrvZeh5" + "RG52EUpE4ms/Ujm3ewV1LoGzc/lYKJAIbdcZQNJ9+06EfWZaIRA3oOwAPe1eCnX+aLr8E" + "6Tw2gDMQOGc5e9HfyXpC2pDvzauoZNYqLALOG3y/1xjo7IH8GYRS2B7zO/Mf9DdCcCKSf" + "w== john.doe@example.com"; private static final String KEY3 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCaS7RHEcZ/zjl9hkWkqnm29RNr2OQ/TZ5" + "jk2qBVMH3BgzPsTsEs+7ag9tfD8OCj+vOcwm626mQBZoR2e3niHa/9gnHBHFtOrGfzKbp" + "RjTWtiOZbB9HF+rqMVD+Dawo/oicX/dDg7VAgOFSPothe6RMhbgWf84UcK5aQd5eP5y+t" + "Q== john.doe@example.com"; private static final String KEY4 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDIJzW9BaAeO+upFletwwEBnGS15lJmS5i" + "08/NiFef0jXtNNKcLtnd13bq8jOi5VA2is0bwof1c8YbwcvUkdFa8RL5aXoyZBpfYZsWs" + "/YBLZGiHy5rjooMZQMnH37A50cBPnXr0AQz0WRBxLDBDyOZho+O/DfYAKv4rzPSQ3yw4+" + "w== john.doe@example.com"; private static final String KEY5 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCgBRKGhiXvY6D9sM+Vbth5Kate57YF7kD" + "rqIyUiYIMJK93/AXc8qR/J/p3OIFQAxvLz1qozAur3j5HaiwvxVU19IiSA0vafdhaDLRi" + "zRuEL5e/QOu9yGq9xkWApCmg6edpWAHG+Bx4AldU78MiZvzoB7gMMdxc9RmZ1gYj/DjxV" + "w== john.doe@example.com"; @Test public void test() throws Exception { List<Optional<AccountSshKey>> keys = new ArrayList<>(); StringBuilder expected = new StringBuilder(); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addKey(keys, KEY1)); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addKey(keys, KEY2)); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addInvalidKey(keys, KEY3)); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addKey(keys, KEY4)); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addDeletedKey(keys)); assertSerialization(keys, expected); assertParse(expected, keys); expected.append(addKey(keys, KEY5)); assertSerialization(keys, expected); assertParse(expected, keys); } @Test public void parseWindowsLineEndings() throws Exception { List<Optional<AccountSshKey>> keys = new ArrayList<>(); StringBuilder authorizedKeys = new StringBuilder(); authorizedKeys.append(toWindowsLineEndings(addKey(keys, KEY1))); assertParse(authorizedKeys, keys); authorizedKeys.append(toWindowsLineEndings(addKey(keys, KEY2))); assertParse(authorizedKeys, keys); authorizedKeys.append(toWindowsLineEndings(addInvalidKey(keys, KEY3))); assertParse(authorizedKeys, keys); authorizedKeys.append(toWindowsLineEndings(addKey(keys, KEY4))); assertParse(authorizedKeys, keys); authorizedKeys.append(toWindowsLineEndings(addDeletedKey(keys))); assertParse(authorizedKeys, keys); authorizedKeys.append(toWindowsLineEndings(addKey(keys, KEY5))); assertParse(authorizedKeys, keys); } private static String toWindowsLineEndings(String s) { return s.replaceAll("\n", "\r\n"); } private static void assertSerialization( List<Optional<AccountSshKey>> keys, StringBuilder expected) { assertThat(AuthorizedKeys.serialize(keys)).isEqualTo(expected.toString()); } private static void assertParse( StringBuilder authorizedKeys, List<Optional<AccountSshKey>> expectedKeys) { Account.Id accountId = new Account.Id(1); List<Optional<AccountSshKey>> parsedKeys = AuthorizedKeys.parse(accountId, authorizedKeys.toString()); assertThat(parsedKeys).containsExactlyElementsIn(expectedKeys); int seq = 1; for (Optional<AccountSshKey> sshKey : parsedKeys) { if (sshKey.isPresent()) { assertThat(sshKey.get().getAccount()).isEqualTo(accountId); assertThat(sshKey.get().getKey().get()).isEqualTo(seq); } seq++; } } /** * Adds the given public key as new SSH key to the given list. * * @return the expected line for this key in the authorized_keys file */ private static String addKey(List<Optional<AccountSshKey>> keys, String pub) { AccountSshKey.Id keyId = new AccountSshKey.Id(new Account.Id(1), keys.size() + 1); AccountSshKey key = new AccountSshKey(keyId, pub); keys.add(Optional.of(key)); return key.getSshPublicKey() + "\n"; } /** * Adds the given public key as invalid SSH key to the given list. * * @return the expected line for this key in the authorized_keys file */ private static String addInvalidKey(List<Optional<AccountSshKey>> keys, String pub) { AccountSshKey.Id keyId = new AccountSshKey.Id(new Account.Id(1), keys.size() + 1); AccountSshKey key = new AccountSshKey(keyId, pub); key.setInvalid(); keys.add(Optional.of(key)); return AuthorizedKeys.INVALID_KEY_COMMENT_PREFIX + key.getSshPublicKey() + "\n"; } /** * Adds a deleted SSH key to the given list. * * @return the expected line for this key in the authorized_keys file */ private static String addDeletedKey(List<Optional<AccountSshKey>> keys) { keys.add(Optional.empty()); return AuthorizedKeys.DELETED_KEY_COMMENT + "\n"; } }