HADOOP-6367. Removes Access Token implementation from common. Contributed by Kan Zhang.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@881509 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cdcb8514a0
commit
bd92322d22
|
@ -653,6 +653,9 @@ Release 0.21.0 - Unreleased
|
|||
HADOOP-6343. Log unexpected throwable object caught in RPC. (Jitendra Nath
|
||||
Pandey via szetszwo)
|
||||
|
||||
HADOOP-6367. Removes Access Token implementation from common.
|
||||
(Kan Zhang via ddas)
|
||||
|
||||
OPTIMIZATIONS
|
||||
|
||||
HADOOP-5595. NameNode does not need to run a replicator to choose a
|
||||
|
|
|
@ -269,29 +269,6 @@
|
|||
<description>Disk usage statistics refresh interval in msec.</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>fs.access.token.enable</name>
|
||||
<value>false</value>
|
||||
<description>
|
||||
If "true", access tokens are used as capabilities for accessing datanodes.
|
||||
If "false", no access tokens are checked on accessing datanodes.
|
||||
</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>fs.access.key.update.interval</name>
|
||||
<value>600</value>
|
||||
<description>
|
||||
Interval in minutes at which namenode updates its access keys.
|
||||
</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>fs.access.token.lifetime</name>
|
||||
<value>600</value>
|
||||
<description>The lifetime of access tokens in minutes.</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>fs.s3.block.size</name>
|
||||
<value>67108864</value>
|
||||
|
|
|
@ -1757,12 +1757,6 @@ public class Configuration implements Iterable<Map.Entry<String,String>>,
|
|||
new String[]{CommonConfigurationKeys.FS_CLIENT_BUFFER_DIR_KEY});
|
||||
Configuration.addDeprecation("hadoop.native.lib",
|
||||
new String[]{CommonConfigurationKeys.IO_NATIVE_LIB_AVAILABLE_KEY});
|
||||
Configuration.addDeprecation("dfs.access.token.enable",
|
||||
new String[]{CommonConfigurationKeys.FS_ACCESS_TOKEN_ENABLE_KEY});
|
||||
Configuration.addDeprecation("dfs.access.key.update.interval",
|
||||
new String[]{CommonConfigurationKeys.FS_ACCESS_KEY_UPDATE_INTERVAL_KEY});
|
||||
Configuration.addDeprecation("dfs.access.token.lifetime",
|
||||
new String[]{CommonConfigurationKeys.FS_ACCESS_TOKEN_LIFETIME_KEY});
|
||||
Configuration.addDeprecation("fs.default.name",
|
||||
new String[]{CommonConfigurationKeys.FS_DEFAULT_NAME_KEY});
|
||||
}
|
||||
|
|
|
@ -43,15 +43,6 @@ public class CommonConfigurationKeys {
|
|||
public static final int FS_PERMISSIONS_UMASK_DEFAULT = 0022;
|
||||
public static final String FS_DF_INTERVAL_KEY = "fs.df.interval";
|
||||
public static final long FS_DF_INTERVAL_DEFAULT = 60000;
|
||||
public static final String FS_ACCESS_TOKEN_ENABLE_KEY =
|
||||
"fs.access.token.enable";
|
||||
public static final boolean FS_ACCESS_TOKEN_ENABLE_DEFAULT = false;
|
||||
public static final String FS_ACCESS_KEY_UPDATE_INTERVAL_KEY =
|
||||
"fs.access.key.update.interval";
|
||||
public static final long FS_ACCESS_KEY_UPDATE_INTERVAL_DEFAULT = 600;
|
||||
public static final String FS_ACCESS_TOKEN_LIFETIME_KEY =
|
||||
"fs.access.token.lifetime";
|
||||
public static final long FS_ACCESS_TOKEN_LIFETIME_DEFAULT = 600;
|
||||
|
||||
|
||||
//Defaults are not specified for following keys
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* 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.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.io.Writable;
|
||||
import org.apache.hadoop.io.WritableUtils;
|
||||
|
||||
/**
|
||||
* Key used for generating and verifying access tokens
|
||||
*/
|
||||
public class AccessKey implements Writable {
|
||||
private long keyID;
|
||||
private Text key;
|
||||
private long expiryDate;
|
||||
private Mac mac;
|
||||
|
||||
public AccessKey() {
|
||||
this(0L, new Text(), 0L);
|
||||
}
|
||||
|
||||
public AccessKey(long keyID, Text key, long expiryDate) {
|
||||
this.keyID = keyID;
|
||||
this.key = key;
|
||||
this.expiryDate = expiryDate;
|
||||
}
|
||||
|
||||
public long getKeyID() {
|
||||
return keyID;
|
||||
}
|
||||
|
||||
public Text getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public long getExpiryDate() {
|
||||
return expiryDate;
|
||||
}
|
||||
|
||||
public Mac getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(Mac mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
static boolean isEqual(Object a, Object b) {
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof AccessKey) {
|
||||
AccessKey that = (AccessKey) obj;
|
||||
return this.keyID == that.keyID && isEqual(this.key, that.key)
|
||||
&& this.expiryDate == that.expiryDate;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public int hashCode() {
|
||||
return key == null ? 0 : key.hashCode();
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////
|
||||
// Writable
|
||||
// ///////////////////////////////////////////////
|
||||
/**
|
||||
*/
|
||||
public void write(DataOutput out) throws IOException {
|
||||
WritableUtils.writeVLong(out, keyID);
|
||||
key.write(out);
|
||||
WritableUtils.writeVLong(out, expiryDate);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
keyID = WritableUtils.readVLong(in);
|
||||
key.readFields(in);
|
||||
expiryDate = WritableUtils.readVLong(in);
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* 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.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.io.Writable;
|
||||
|
||||
public class AccessToken implements Writable {
|
||||
public static final AccessToken DUMMY_TOKEN = new AccessToken();
|
||||
private Text tokenID;
|
||||
private Text tokenAuthenticator;
|
||||
|
||||
public AccessToken() {
|
||||
this(new Text(), new Text());
|
||||
}
|
||||
|
||||
public AccessToken(Text tokenID, Text tokenAuthenticator) {
|
||||
this.tokenID = tokenID;
|
||||
this.tokenAuthenticator = tokenAuthenticator;
|
||||
}
|
||||
|
||||
public Text getTokenID() {
|
||||
return tokenID;
|
||||
}
|
||||
|
||||
public Text getTokenAuthenticator() {
|
||||
return tokenAuthenticator;
|
||||
}
|
||||
|
||||
static boolean isEqual(Object a, Object b) {
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof AccessToken) {
|
||||
AccessToken that = (AccessToken) obj;
|
||||
return isEqual(this.tokenID, that.tokenID)
|
||||
&& isEqual(this.tokenAuthenticator, that.tokenAuthenticator);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public int hashCode() {
|
||||
return tokenAuthenticator == null ? 0 : tokenAuthenticator.hashCode();
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////
|
||||
// Writable
|
||||
// ///////////////////////////////////////////////
|
||||
/**
|
||||
*/
|
||||
public void write(DataOutput out) throws IOException {
|
||||
tokenID.write(out);
|
||||
tokenAuthenticator.write(out);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
tokenID.readFields(in);
|
||||
tokenAuthenticator.readFields(in);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,312 +0,0 @@
|
|||
/**
|
||||
* 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.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.io.WritableUtils;
|
||||
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
||||
|
||||
/**
|
||||
* AccessTokenHandler can be instantiated in 2 modes, master mode and slave
|
||||
* mode. Master can generate new access keys and export access keys to slaves,
|
||||
* while slaves can only import and use access keys received from master. Both
|
||||
* master and slave can generate and verify access tokens. Typically, master
|
||||
* mode is used by NN and slave mode is used by DN.
|
||||
*/
|
||||
public class AccessTokenHandler {
|
||||
private static final Log LOG = LogFactory.getLog(AccessTokenHandler.class);
|
||||
public static final String STRING_ENABLE_ACCESS_TOKEN =
|
||||
CommonConfigurationKeys.FS_ACCESS_TOKEN_ENABLE_KEY;
|
||||
public static final String STRING_ACCESS_KEY_UPDATE_INTERVAL =
|
||||
CommonConfigurationKeys.FS_ACCESS_KEY_UPDATE_INTERVAL_KEY;
|
||||
public static final String STRING_ACCESS_TOKEN_LIFETIME =
|
||||
CommonConfigurationKeys.FS_ACCESS_TOKEN_LIFETIME_KEY;
|
||||
|
||||
|
||||
private final boolean isMaster;
|
||||
/*
|
||||
* keyUpdateInterval is the interval that NN updates its access keys. It
|
||||
* should be set long enough so that all live DN's and Balancer should have
|
||||
* sync'ed their access keys with NN at least once during each interval.
|
||||
*/
|
||||
private final long keyUpdateInterval;
|
||||
private long tokenLifetime;
|
||||
private long serialNo = new SecureRandom().nextLong();
|
||||
private KeyGenerator keyGen;
|
||||
private AccessKey currentKey;
|
||||
private AccessKey nextKey;
|
||||
private Map<Long, AccessKey> allKeys;
|
||||
|
||||
public static enum AccessMode {
|
||||
READ, WRITE, COPY, REPLACE
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param isMaster
|
||||
* @param keyUpdateInterval
|
||||
* @param tokenLifetime
|
||||
* @throws IOException
|
||||
*/
|
||||
public AccessTokenHandler(boolean isMaster, long keyUpdateInterval,
|
||||
long tokenLifetime) throws IOException {
|
||||
this.isMaster = isMaster;
|
||||
this.keyUpdateInterval = keyUpdateInterval;
|
||||
this.tokenLifetime = tokenLifetime;
|
||||
this.allKeys = new HashMap<Long, AccessKey>();
|
||||
if (isMaster) {
|
||||
try {
|
||||
generateKeys();
|
||||
initMac(currentKey);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw (IOException) new IOException(
|
||||
"Failed to create AccessTokenHandler").initCause(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize access keys */
|
||||
private synchronized void generateKeys() throws NoSuchAlgorithmException {
|
||||
keyGen = KeyGenerator.getInstance("HmacSHA1");
|
||||
/*
|
||||
* Need to set estimated expiry dates for currentKey and nextKey so that if
|
||||
* NN crashes, DN can still expire those keys. NN will stop using the newly
|
||||
* generated currentKey after the first keyUpdateInterval, however it may
|
||||
* still be used by DN and Balancer to generate new tokens before they get a
|
||||
* chance to sync their keys with NN. Since we require keyUpdInterval to be
|
||||
* long enough so that all live DN's and Balancer will sync their keys with
|
||||
* NN at least once during the period, the estimated expiry date for
|
||||
* currentKey is set to now() + 2 * keyUpdateInterval + tokenLifetime.
|
||||
* Similarly, the estimated expiry date for nextKey is one keyUpdateInterval
|
||||
* more.
|
||||
*/
|
||||
serialNo++;
|
||||
currentKey = new AccessKey(serialNo, new Text(keyGen.generateKey()
|
||||
.getEncoded()), System.currentTimeMillis() + 2 * keyUpdateInterval
|
||||
+ tokenLifetime);
|
||||
serialNo++;
|
||||
nextKey = new AccessKey(serialNo, new Text(keyGen.generateKey()
|
||||
.getEncoded()), System.currentTimeMillis() + 3 * keyUpdateInterval
|
||||
+ tokenLifetime);
|
||||
allKeys.put(currentKey.getKeyID(), currentKey);
|
||||
allKeys.put(nextKey.getKeyID(), nextKey);
|
||||
}
|
||||
|
||||
/** Initialize Mac function */
|
||||
private synchronized void initMac(AccessKey key) throws IOException {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(new SecretKeySpec(key.getKey().getBytes(), "HmacSHA1"));
|
||||
key.setMac(mac);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw (IOException) new IOException(
|
||||
"Failed to initialize Mac for access key, keyID=" + key.getKeyID())
|
||||
.initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Export access keys, only to be used in master mode */
|
||||
public synchronized ExportedAccessKeys exportKeys() {
|
||||
if (!isMaster)
|
||||
return null;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Exporting access keys");
|
||||
return new ExportedAccessKeys(true, keyUpdateInterval, tokenLifetime,
|
||||
currentKey, allKeys.values().toArray(new AccessKey[0]));
|
||||
}
|
||||
|
||||
private synchronized void removeExpiredKeys() {
|
||||
long now = System.currentTimeMillis();
|
||||
for (Iterator<Map.Entry<Long, AccessKey>> it = allKeys.entrySet()
|
||||
.iterator(); it.hasNext();) {
|
||||
Map.Entry<Long, AccessKey> e = it.next();
|
||||
if (e.getValue().getExpiryDate() < now) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set access keys, only to be used in slave mode
|
||||
*/
|
||||
public synchronized void setKeys(ExportedAccessKeys exportedKeys)
|
||||
throws IOException {
|
||||
if (isMaster || exportedKeys == null)
|
||||
return;
|
||||
LOG.info("Setting access keys");
|
||||
removeExpiredKeys();
|
||||
this.currentKey = exportedKeys.getCurrentKey();
|
||||
initMac(currentKey);
|
||||
AccessKey[] receivedKeys = exportedKeys.getAllKeys();
|
||||
for (int i = 0; i < receivedKeys.length; i++) {
|
||||
if (receivedKeys[i] == null)
|
||||
continue;
|
||||
this.allKeys.put(receivedKeys[i].getKeyID(), receivedKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update access keys, only to be used in master mode
|
||||
*/
|
||||
public synchronized void updateKeys() throws IOException {
|
||||
if (!isMaster)
|
||||
return;
|
||||
LOG.info("Updating access keys");
|
||||
removeExpiredKeys();
|
||||
// set final expiry date of retiring currentKey
|
||||
allKeys.put(currentKey.getKeyID(), new AccessKey(currentKey.getKeyID(),
|
||||
currentKey.getKey(), System.currentTimeMillis() + keyUpdateInterval
|
||||
+ tokenLifetime));
|
||||
// update the estimated expiry date of new currentKey
|
||||
currentKey = new AccessKey(nextKey.getKeyID(), nextKey.getKey(), System
|
||||
.currentTimeMillis()
|
||||
+ 2 * keyUpdateInterval + tokenLifetime);
|
||||
initMac(currentKey);
|
||||
allKeys.put(currentKey.getKeyID(), currentKey);
|
||||
// generate a new nextKey
|
||||
serialNo++;
|
||||
nextKey = new AccessKey(serialNo, new Text(keyGen.generateKey()
|
||||
.getEncoded()), System.currentTimeMillis() + 3 * keyUpdateInterval
|
||||
+ tokenLifetime);
|
||||
allKeys.put(nextKey.getKeyID(), nextKey);
|
||||
}
|
||||
|
||||
/** Check if token is well formed */
|
||||
private synchronized boolean verifyToken(long keyID, AccessToken token)
|
||||
throws IOException {
|
||||
AccessKey key = allKeys.get(keyID);
|
||||
if (key == null) {
|
||||
LOG.warn("Access key for keyID=" + keyID + " doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
if (key.getMac() == null) {
|
||||
initMac(key);
|
||||
}
|
||||
Text tokenID = token.getTokenID();
|
||||
Text authenticator = new Text(key.getMac().doFinal(tokenID.getBytes()));
|
||||
return authenticator.equals(token.getTokenAuthenticator());
|
||||
}
|
||||
|
||||
/** Generate an access token for current user */
|
||||
public AccessToken generateToken(long blockID, EnumSet<AccessMode> modes)
|
||||
throws IOException {
|
||||
UserGroupInformation ugi = UserGroupInformation.getCurrentUGI();
|
||||
String userID = (ugi == null ? null : ugi.getUserName());
|
||||
return generateToken(userID, blockID, modes);
|
||||
}
|
||||
|
||||
/** Generate an access token for a specified user */
|
||||
public synchronized AccessToken generateToken(String userID, long blockID,
|
||||
EnumSet<AccessMode> modes) throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Generating access token for user=" + userID + ", blockID="
|
||||
+ blockID + ", access modes=" + modes + ", keyID="
|
||||
+ currentKey.getKeyID());
|
||||
}
|
||||
if (modes == null || modes.isEmpty())
|
||||
throw new IOException("access modes can't be null or empty");
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);
|
||||
DataOutputStream out = new DataOutputStream(buf);
|
||||
WritableUtils.writeVLong(out, System.currentTimeMillis() + tokenLifetime);
|
||||
WritableUtils.writeVLong(out, currentKey.getKeyID());
|
||||
WritableUtils.writeString(out, userID);
|
||||
WritableUtils.writeVLong(out, blockID);
|
||||
WritableUtils.writeVInt(out, modes.size());
|
||||
for (AccessMode aMode : modes) {
|
||||
WritableUtils.writeEnum(out, aMode);
|
||||
}
|
||||
Text tokenID = new Text(buf.toByteArray());
|
||||
return new AccessToken(tokenID, new Text(currentKey.getMac().doFinal(
|
||||
tokenID.getBytes())));
|
||||
}
|
||||
|
||||
/** Check if access should be allowed. userID is not checked if null */
|
||||
public boolean checkAccess(AccessToken token, String userID, long blockID,
|
||||
AccessMode mode) throws IOException {
|
||||
long oExpiry = 0;
|
||||
long oKeyID = 0;
|
||||
String oUserID = null;
|
||||
long oBlockID = 0;
|
||||
EnumSet<AccessMode> oModes = EnumSet.noneOf(AccessMode.class);
|
||||
|
||||
try {
|
||||
ByteArrayInputStream buf = new ByteArrayInputStream(token.getTokenID()
|
||||
.getBytes());
|
||||
DataInputStream in = new DataInputStream(buf);
|
||||
oExpiry = WritableUtils.readVLong(in);
|
||||
oKeyID = WritableUtils.readVLong(in);
|
||||
oUserID = WritableUtils.readString(in);
|
||||
oBlockID = WritableUtils.readVLong(in);
|
||||
int length = WritableUtils.readVInt(in);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
oModes.add(WritableUtils.readEnum(in, AccessMode.class));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw (IOException) new IOException(
|
||||
"Unable to parse access token for user=" + userID + ", blockID="
|
||||
+ blockID + ", access mode=" + mode).initCause(e);
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Verifying access token for user=" + userID + ", blockID="
|
||||
+ blockID + ", access mode=" + mode + ", keyID=" + oKeyID);
|
||||
}
|
||||
return (userID == null || userID.equals(oUserID)) && oBlockID == blockID
|
||||
&& !isExpired(oExpiry) && oModes.contains(mode)
|
||||
&& verifyToken(oKeyID, token);
|
||||
}
|
||||
|
||||
private static boolean isExpired(long expiryDate) {
|
||||
return System.currentTimeMillis() > expiryDate;
|
||||
}
|
||||
|
||||
/** check if a token is expired. for unit test only.
|
||||
* return true when token is expired, false otherwise */
|
||||
static boolean isTokenExpired(AccessToken token) throws IOException {
|
||||
ByteArrayInputStream buf = new ByteArrayInputStream(token.getTokenID()
|
||||
.getBytes());
|
||||
DataInputStream in = new DataInputStream(buf);
|
||||
long expiryDate = WritableUtils.readVLong(in);
|
||||
return isExpired(expiryDate);
|
||||
}
|
||||
|
||||
/** set token lifetime. for unit test only */
|
||||
synchronized void setTokenLifetime(long tokenLifetime) {
|
||||
this.tokenLifetime = tokenLifetime;
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/**
|
||||
* 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.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.hadoop.io.Writable;
|
||||
import org.apache.hadoop.io.WritableFactories;
|
||||
import org.apache.hadoop.io.WritableFactory;
|
||||
|
||||
/**
|
||||
* Object for passing access keys
|
||||
*/
|
||||
public class ExportedAccessKeys implements Writable {
|
||||
public static final ExportedAccessKeys DUMMY_KEYS = new ExportedAccessKeys();
|
||||
private boolean isAccessTokenEnabled;
|
||||
private long keyUpdateInterval;
|
||||
private long tokenLifetime;
|
||||
private AccessKey currentKey;
|
||||
private AccessKey[] allKeys;
|
||||
|
||||
public ExportedAccessKeys() {
|
||||
this(false, 0, 0, new AccessKey(), new AccessKey[0]);
|
||||
}
|
||||
|
||||
ExportedAccessKeys(boolean isAccessTokenEnabled, long keyUpdateInterval,
|
||||
long tokenLifetime, AccessKey currentKey, AccessKey[] allKeys) {
|
||||
this.isAccessTokenEnabled = isAccessTokenEnabled;
|
||||
this.keyUpdateInterval = keyUpdateInterval;
|
||||
this.tokenLifetime = tokenLifetime;
|
||||
this.currentKey = currentKey;
|
||||
this.allKeys = allKeys;
|
||||
}
|
||||
|
||||
public boolean isAccessTokenEnabled() {
|
||||
return isAccessTokenEnabled;
|
||||
}
|
||||
|
||||
public long getKeyUpdateInterval() {
|
||||
return keyUpdateInterval;
|
||||
}
|
||||
|
||||
public long getTokenLifetime() {
|
||||
return tokenLifetime;
|
||||
}
|
||||
|
||||
public AccessKey getCurrentKey() {
|
||||
return currentKey;
|
||||
}
|
||||
|
||||
public AccessKey[] getAllKeys() {
|
||||
return allKeys;
|
||||
}
|
||||
|
||||
static boolean isEqual(Object a, Object b) {
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof ExportedAccessKeys) {
|
||||
ExportedAccessKeys that = (ExportedAccessKeys) obj;
|
||||
return this.isAccessTokenEnabled == that.isAccessTokenEnabled
|
||||
&& this.keyUpdateInterval == that.keyUpdateInterval
|
||||
&& this.tokenLifetime == that.tokenLifetime
|
||||
&& isEqual(this.currentKey, that.currentKey)
|
||||
&& Arrays.equals(this.allKeys, that.allKeys);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public int hashCode() {
|
||||
return currentKey == null ? 0 : currentKey.hashCode();
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////
|
||||
// Writable
|
||||
// ///////////////////////////////////////////////
|
||||
static { // register a ctor
|
||||
WritableFactories.setFactory(ExportedAccessKeys.class,
|
||||
new WritableFactory() {
|
||||
public Writable newInstance() {
|
||||
return new ExportedAccessKeys();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeBoolean(isAccessTokenEnabled);
|
||||
out.writeLong(keyUpdateInterval);
|
||||
out.writeLong(tokenLifetime);
|
||||
currentKey.write(out);
|
||||
out.writeInt(allKeys.length);
|
||||
for (int i = 0; i < allKeys.length; i++) {
|
||||
allKeys[i].write(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
isAccessTokenEnabled = in.readBoolean();
|
||||
keyUpdateInterval = in.readLong();
|
||||
tokenLifetime = in.readLong();
|
||||
currentKey.readFields(in);
|
||||
this.allKeys = new AccessKey[in.readInt()];
|
||||
for (int i = 0; i < allKeys.length; i++) {
|
||||
allKeys[i] = new AccessKey();
|
||||
allKeys[i].readFields(in);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* 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.IOException;
|
||||
|
||||
/**
|
||||
* Access token verification failed.
|
||||
*/
|
||||
public class InvalidAccessTokenException extends IOException {
|
||||
private static final long serialVersionUID = 168L;
|
||||
|
||||
public InvalidAccessTokenException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public InvalidAccessTokenException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* 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.IOException;
|
||||
|
||||
/** Utilities for security tests */
|
||||
public class SecurityTestUtil {
|
||||
|
||||
/**
|
||||
* check if an access token is expired. return true when token is expired,
|
||||
* false otherwise
|
||||
*/
|
||||
public static boolean isAccessTokenExpired(AccessToken token)
|
||||
throws IOException {
|
||||
return AccessTokenHandler.isTokenExpired(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* set access token lifetime.
|
||||
*/
|
||||
public static void setAccessTokenLifetime(AccessTokenHandler handler,
|
||||
long tokenLifetime) {
|
||||
handler.setTokenLifetime(tokenLifetime);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* 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.util.EnumSet;
|
||||
|
||||
import org.apache.hadoop.io.TestWritable;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/** Unit tests for access tokens */
|
||||
public class TestAccessToken extends TestCase {
|
||||
long accessKeyUpdateInterval = 10 * 60 * 1000; // 10 mins
|
||||
long accessTokenLifetime = 2 * 60 * 1000; // 2 mins
|
||||
long blockID1 = 0L;
|
||||
long blockID2 = 10L;
|
||||
long blockID3 = -108L;
|
||||
|
||||
/** test Writable */
|
||||
public void testWritable() throws Exception {
|
||||
TestWritable.testWritable(ExportedAccessKeys.DUMMY_KEYS);
|
||||
AccessTokenHandler handler = new AccessTokenHandler(true,
|
||||
accessKeyUpdateInterval, accessTokenLifetime);
|
||||
ExportedAccessKeys keys = handler.exportKeys();
|
||||
TestWritable.testWritable(keys);
|
||||
TestWritable.testWritable(AccessToken.DUMMY_TOKEN);
|
||||
AccessToken token = handler.generateToken(blockID3, EnumSet
|
||||
.allOf(AccessTokenHandler.AccessMode.class));
|
||||
TestWritable.testWritable(token);
|
||||
}
|
||||
|
||||
private void tokenGenerationAndVerification(AccessTokenHandler master,
|
||||
AccessTokenHandler slave) throws Exception {
|
||||
// single-mode tokens
|
||||
for (AccessTokenHandler.AccessMode mode : AccessTokenHandler.AccessMode
|
||||
.values()) {
|
||||
// generated by master
|
||||
AccessToken token1 = master.generateToken(blockID1, EnumSet.of(mode));
|
||||
assertTrue(master.checkAccess(token1, null, blockID1, mode));
|
||||
assertTrue(slave.checkAccess(token1, null, blockID1, mode));
|
||||
// generated by slave
|
||||
AccessToken token2 = slave.generateToken(blockID2, EnumSet.of(mode));
|
||||
assertTrue(master.checkAccess(token2, null, blockID2, mode));
|
||||
assertTrue(slave.checkAccess(token2, null, blockID2, mode));
|
||||
}
|
||||
// multi-mode tokens
|
||||
AccessToken mtoken = master.generateToken(blockID3, EnumSet
|
||||
.allOf(AccessTokenHandler.AccessMode.class));
|
||||
for (AccessTokenHandler.AccessMode mode : AccessTokenHandler.AccessMode
|
||||
.values()) {
|
||||
assertTrue(master.checkAccess(mtoken, null, blockID3, mode));
|
||||
assertTrue(slave.checkAccess(mtoken, null, blockID3, mode));
|
||||
}
|
||||
}
|
||||
|
||||
/** test access key and token handling */
|
||||
public void testAccessTokenHandler() throws Exception {
|
||||
AccessTokenHandler masterHandler = new AccessTokenHandler(true,
|
||||
accessKeyUpdateInterval, accessTokenLifetime);
|
||||
AccessTokenHandler slaveHandler = new AccessTokenHandler(false,
|
||||
accessKeyUpdateInterval, accessTokenLifetime);
|
||||
ExportedAccessKeys keys = masterHandler.exportKeys();
|
||||
slaveHandler.setKeys(keys);
|
||||
tokenGenerationAndVerification(masterHandler, slaveHandler);
|
||||
// key updating
|
||||
masterHandler.updateKeys();
|
||||
tokenGenerationAndVerification(masterHandler, slaveHandler);
|
||||
keys = masterHandler.exportKeys();
|
||||
slaveHandler.setKeys(keys);
|
||||
tokenGenerationAndVerification(masterHandler, slaveHandler);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue