TestCredentials.java
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.security;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import javax.crypto.KeyGenerator;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.test.GenericTestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestCredentials {
private static final String DEFAULT_HMAC_ALGORITHM = "HmacSHA1";
private static final File tmpDir = GenericTestUtils.getTestDir("mapred");
@BeforeEach
public void setUp() {
tmpDir.mkdir();
}
@AfterEach
public void tearDown() {
tmpDir.delete();
}
@SuppressWarnings("unchecked")
@Test
public <T extends TokenIdentifier> void testReadWriteStorage()
throws IOException, NoSuchAlgorithmException{
// create tokenStorage Object
Credentials ts = new Credentials();
Token<T> token1 = new Token();
Token<T> token2 = new Token();
Text service1 = new Text("service1");
Text service2 = new Text("service2");
Text alias1 = new Text("sometoken1");
Text alias2 = new Text("sometoken2");
Collection<Text> services = new ArrayList<Text>();
services.add(service1);
services.add(service2);
token1.setService(service1);
token2.setService(service2);
ts.addToken(alias1, token1);
ts.addToken(alias2, token2);
// create keys and put it in
final KeyGenerator kg = KeyGenerator.getInstance(DEFAULT_HMAC_ALGORITHM);
String alias = "alias";
Map<Text, byte[]> m = new HashMap<Text, byte[]>(10);
for(int i=0; i<10; i++) {
Key key = kg.generateKey();
m.put(new Text(alias+i), key.getEncoded());
ts.addSecretKey(new Text(alias+i), key.getEncoded());
}
// create file to store
File tmpFileName = new File(tmpDir, "tokenStorageTest");
DataOutputStream dos =
new DataOutputStream(new FileOutputStream(tmpFileName));
ts.write(dos);
dos.close();
// open and read it back
DataInputStream dis =
new DataInputStream(new FileInputStream(tmpFileName));
ts = new Credentials();
ts.readFields(dis);
dis.close();
// get the tokens and compare the services
Map<Text, Token<? extends TokenIdentifier>> tokenMap = ts.getTokenMap();
assertEquals(2, tokenMap.size(),
"getTokenMap should return collection of size 2");
assertTrue(tokenMap.containsKey(alias1),
"Token for alias " + alias1 + " must be present");
assertTrue(tokenMap.containsKey(alias2), "Token for alias " + alias2 + " must be present");
assertEquals(service1, tokenMap.get(alias1).getService(),
"Token for service " + service1 + " must be present");
assertEquals(service2,
tokenMap.get(alias2).getService(), "Token for service " + service2 + " must be present");
// compare secret keys
Map<Text, byte[]> secretKeyMap = ts.getSecretKeyMap();
assertEquals(m.size(), ts.numberOfSecretKeys(),
"wrong number of keys in the Storage");
for (Map.Entry<Text, byte[]> entry : m.entrySet()) {
byte[] key = secretKeyMap.get(entry.getKey());
assertNotNull(key, "Secret key for alias " + entry.getKey() + " not found");
assertTrue(Arrays.equals(key, entry.getValue()),
"Keys don't match for alias " + entry.getKey());
}
tmpFileName.delete();
}
@Test
public void testBasicReadWriteProtoEmpty()
throws IOException, NoSuchAlgorithmException {
String testname ="testBasicReadWriteProtoEmpty";
Credentials ts = new Credentials();
writeCredentialsProto(ts, testname);
Credentials ts2 = readCredentialsProto(testname);
assertEquals(0, ts2.numberOfTokens(), "test empty tokens");
assertEquals(0, ts2.numberOfSecretKeys(), "test empty keys");
}
@Test
public void testBasicReadWriteProto()
throws IOException, NoSuchAlgorithmException {
String testname ="testBasicReadWriteProto";
Text tok1 = new Text("token1");
Text tok2 = new Text("token2");
Text key1 = new Text("key1");
Credentials ts = generateCredentials(tok1, tok2, key1);
writeCredentialsProto(ts, testname);
Credentials ts2 = readCredentialsProto(testname);
assertCredentials(testname, tok1, key1, ts, ts2);
assertCredentials(testname, tok2, key1, ts, ts2);
}
@Test
public void testBasicReadWriteStreamEmpty()
throws IOException, NoSuchAlgorithmException {
String testname ="testBasicReadWriteStreamEmpty";
Credentials ts = new Credentials();
writeCredentialsStream(ts, testname);
Credentials ts2 = readCredentialsStream(testname);
assertEquals(0, ts2.numberOfTokens(), "test empty tokens");
assertEquals(0, ts2.numberOfSecretKeys(), "test empty keys");
}
@Test
public void testBasicReadWriteStream()
throws IOException, NoSuchAlgorithmException {
String testname ="testBasicReadWriteStream";
Text tok1 = new Text("token1");
Text tok2 = new Text("token2");
Text key1 = new Text("key1");
Credentials ts = generateCredentials(tok1, tok2, key1);
writeCredentialsStream(ts, testname);
Credentials ts2 = readCredentialsStream(testname);
assertCredentials(testname, tok1, key1, ts, ts2);
assertCredentials(testname, tok2, key1, ts, ts2);
}
@Test
/**
* Verify the suitability of read/writeProto for use with Writable interface.
* This test uses only empty credentials.
*/
public void testWritablePropertiesEmpty()
throws IOException, NoSuchAlgorithmException {
String testname ="testWritablePropertiesEmpty";
Credentials ts = new Credentials();
Credentials ts2 = new Credentials();
writeCredentialsProtos(ts, ts2, testname);
List<Credentials> clist = readCredentialsProtos(testname);
assertEquals(0, clist.get(0).numberOfTokens(), "test empty tokens 0");
assertEquals(0, clist.get(0).numberOfSecretKeys(), "test empty keys 0");
assertEquals(0, clist.get(1).numberOfTokens(), "test empty tokens 1");
assertEquals(0, clist.get(1).numberOfSecretKeys(), "test empty keys 1");
}
@Test
/**
* Verify the suitability of read/writeProto for use with Writable interface.
*/
public void testWritableProperties()
throws IOException, NoSuchAlgorithmException {
String testname ="testWritableProperties";
Text tok1 = new Text("token1");
Text tok2 = new Text("token2");
Text key1 = new Text("key1");
Credentials ts = generateCredentials(tok1, tok2, key1);
Text tok3 = new Text("token3");
Text key2 = new Text("key2");
Credentials ts2 = generateCredentials(tok1, tok3, key2);
writeCredentialsProtos(ts, ts2, testname);
List<Credentials> clist = readCredentialsProtos(testname);
assertCredentials(testname, tok1, key1, ts, clist.get(0));
assertCredentials(testname, tok2, key1, ts, clist.get(0));
assertCredentials(testname, tok1, key2, ts2, clist.get(1));
assertCredentials(testname, tok3, key2, ts2, clist.get(1));
}
private Credentials generateCredentials(Text t1, Text t2, Text t3)
throws NoSuchAlgorithmException {
Text kind = new Text("TESTTOK");
byte[] id1 = {0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72};
byte[] pass1 = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64};
byte[] id2 = {0x68, 0x63, 0x64, 0x6d, 0x73, 0x68, 0x65, 0x68, 0x64, 0x71};
byte[] pass2 = {0x6f, 0x60, 0x72, 0x72, 0x76, 0x6e, 0x71, 0x63};
Credentials ts = new Credentials();
generateToken(ts, id1, pass1, kind, t1);
generateToken(ts, id2, pass2, kind, t2);
generateKey(ts, t3);
return ts;
}
private void assertCredentials(String tag, Text alias, Text keykey,
Credentials a, Credentials b) {
assertEquals(a.numberOfTokens(), b.numberOfTokens(), tag + ": test token count");
assertEquals(a.getToken(alias).getService(),
b.getToken(alias).getService(), tag + ": test service");
assertEquals(a.getToken(alias).getKind(),
b.getToken(alias).getKind(), tag + ": test kind");
assertTrue(Arrays.equals(a.getToken(alias).getPassword(),
b.getToken(alias).getPassword()), tag + ": test password");
assertTrue(Arrays.equals(a.getToken(alias).getIdentifier(),
b.getToken(alias).getIdentifier()), tag + ": test identifier");
assertEquals(a.numberOfSecretKeys(),
b.numberOfSecretKeys(), tag + ": test number of keys");
assertTrue(Arrays.equals(a.getSecretKey(keykey),
b.getSecretKey(keykey)), tag + ":test key values");
}
private void writeCredentialsStream(Credentials creds, String filename)
throws IOException, FileNotFoundException {
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(new File(tmpDir, filename)));
creds.writeTokenStorageToStream(dos);
}
private Credentials readCredentialsStream(String filename)
throws IOException, FileNotFoundException {
Credentials creds = new Credentials();
DataInputStream dis = new DataInputStream(
new FileInputStream(new File(tmpDir, filename)));
creds.readTokenStorageStream(dis);
return creds;
}
private void writeCredentialsProto(Credentials creds, String filename)
throws IOException, FileNotFoundException {
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(new File(tmpDir, filename)));
creds.writeProto(dos);
}
private Credentials readCredentialsProto(String filename)
throws IOException, FileNotFoundException {
Credentials creds = new Credentials();
DataInputStream dis = new DataInputStream(
new FileInputStream(new File(tmpDir, filename)));
creds.readProto(dis);
return creds;
}
private void writeCredentialsProtos(Credentials c1, Credentials c2,
String filename) throws IOException, FileNotFoundException {
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(new File(tmpDir, filename)));
c1.writeProto(dos);
c2.writeProto(dos);
}
private List<Credentials> readCredentialsProtos(String filename)
throws IOException, FileNotFoundException {
Credentials c1 = new Credentials();
Credentials c2 = new Credentials();
DataInputStream dis = new DataInputStream(
new FileInputStream(new File(tmpDir, filename)));
c1.readProto(dis);
c2.readProto(dis);
List<Credentials> r = new ArrayList<Credentials>(2);
r.add(0, c1);
r.add(1, c2);
return r;
}
private <T extends TokenIdentifier> void generateToken(
Credentials creds, byte[] ident, byte[] pass, Text kind, Text service) {
Token<T> token = new Token(ident, pass, kind, service);
creds.addToken(service, token);
}
private void generateKey(Credentials creds, Text alias)
throws NoSuchAlgorithmException {
final KeyGenerator kg = KeyGenerator.getInstance(DEFAULT_HMAC_ALGORITHM);
Key key = kg.generateKey();
creds.addSecretKey(alias, key.getEncoded());
}
static Text secret[] = {
new Text("secret1"),
new Text("secret2"),
new Text("secret3"),
new Text("secret4")
};
static Text service[] = {
new Text("service1"),
new Text("service2"),
new Text("service3"),
new Text("service4")
};
static Token<?> token[] = {
new Token<TokenIdentifier>(),
new Token<TokenIdentifier>(),
new Token<TokenIdentifier>(),
new Token<TokenIdentifier>()
};
@Test
public void addAll() {
Credentials creds = new Credentials();
creds.addToken(service[0], token[0]);
creds.addToken(service[1], token[1]);
creds.addSecretKey(secret[0], secret[0].getBytes());
creds.addSecretKey(secret[1], secret[1].getBytes());
Credentials credsToAdd = new Credentials();
// one duplicate with different value, one new
credsToAdd.addToken(service[0], token[3]);
credsToAdd.addToken(service[2], token[2]);
credsToAdd.addSecretKey(secret[0], secret[3].getBytes());
credsToAdd.addSecretKey(secret[2], secret[2].getBytes());
creds.addAll(credsToAdd);
assertEquals(3, creds.numberOfTokens());
assertEquals(3, creds.numberOfSecretKeys());
// existing token & secret should be overwritten
assertEquals(token[3], creds.getToken(service[0]));
assertEquals(secret[3], new Text(creds.getSecretKey(secret[0])));
// non-duplicate token & secret should be present
assertEquals(token[1], creds.getToken(service[1]));
assertEquals(secret[1], new Text(creds.getSecretKey(secret[1])));
// new token & secret should be added
assertEquals(token[2], creds.getToken(service[2]));
assertEquals(secret[2], new Text(creds.getSecretKey(secret[2])));
}
@Test
public void mergeAll() {
Credentials creds = new Credentials();
creds.addToken(service[0], token[0]);
creds.addToken(service[1], token[1]);
creds.addSecretKey(secret[0], secret[0].getBytes());
creds.addSecretKey(secret[1], secret[1].getBytes());
Credentials credsToAdd = new Credentials();
// one duplicate with different value, one new
credsToAdd.addToken(service[0], token[3]);
credsToAdd.addToken(service[2], token[2]);
credsToAdd.addSecretKey(secret[0], secret[3].getBytes());
credsToAdd.addSecretKey(secret[2], secret[2].getBytes());
creds.mergeAll(credsToAdd);
assertEquals(3, creds.numberOfTokens());
assertEquals(3, creds.numberOfSecretKeys());
// existing token & secret should not be overwritten
assertEquals(token[0], creds.getToken(service[0]));
assertEquals(secret[0], new Text(creds.getSecretKey(secret[0])));
// non-duplicate token & secret should be present
assertEquals(token[1], creds.getToken(service[1]));
assertEquals(secret[1], new Text(creds.getSecretKey(secret[1])));
// new token & secret should be added
assertEquals(token[2], creds.getToken(service[2]));
assertEquals(secret[2], new Text(creds.getSecretKey(secret[2])));
}
@Test
public void testAddTokensToUGI() {
UserGroupInformation ugi = UserGroupInformation.createRemoteUser("someone");
Credentials creds = new Credentials();
for (int i=0; i < service.length; i++) {
creds.addToken(service[i], token[i]);
}
ugi.addCredentials(creds);
creds = ugi.getCredentials();
for (int i=0; i < service.length; i++) {
assertSame(token[i], creds.getToken(service[i]));
}
assertEquals(service.length, creds.numberOfTokens());
}
}