hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From x...@apache.org
Subject [50/50] [abbrv] hadoop git commit: HDDS-804. Block token: Add secret token manager. Contributed by Ajay Kumar.
Date Thu, 29 Nov 2018 20:45:06 GMT
HDDS-804. Block token: Add secret token manager. Contributed by Ajay Kumar.


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/187bbbe6
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/187bbbe6
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/187bbbe6

Branch: refs/heads/HDDS-4
Commit: 187bbbe68cc87729f327e5ed614474d1269b8d85
Parents: 87f51d2
Author: Ajay Kumar <ajay@apache.org>
Authored: Thu Nov 29 08:00:41 2018 -0800
Committer: Xiaoyu Yao <xyao@apache.org>
Committed: Thu Nov 29 11:58:55 2018 -0800

----------------------------------------------------------------------
 .../hdds/security/x509/SecurityConfig.java      |   9 +
 .../security/OzoneBlockTokenSecretManager.java  | 191 +++++++
 .../OzoneDelegationTokenSecretManager.java      | 455 +++++++++++++++++
 .../ozone/security/OzoneSecretManager.java      | 498 ++++---------------
 .../TestOzoneBlockTokenSecretManager.java       | 146 ++++++
 .../TestOzoneDelegationTokenSecretManager.java  | 218 ++++++++
 .../ozone/security/TestOzoneSecretManager.java  | 216 --------
 .../apache/hadoop/ozone/om/OzoneManager.java    |  23 +-
 .../security/TestOzoneManagerBlockToken.java    | 251 ++++++++++
 9 files changed, 1371 insertions(+), 636 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
index ee20a21..b38ee7c 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
@@ -21,6 +21,7 @@ package org.apache.hadoop.hdds.security.x509;
 
 import com.google.common.base.Preconditions;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.ozone.OzoneConfigKeys;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -77,6 +78,7 @@ public class SecurityConfig {
   private final Duration certDuration;
   private final String x509SignatureAlgo;
   private final Boolean grpcBlockTokenEnabled;
+  private final int getMaxKeyLength;
   private final String certificateDir;
   private final String certificateFileName;
 
@@ -88,6 +90,9 @@ public class SecurityConfig {
   public SecurityConfig(Configuration configuration) {
     Preconditions.checkNotNull(configuration, "Configuration cannot be null");
     this.configuration = configuration;
+    this.getMaxKeyLength = configuration.getInt(
+        OzoneConfigKeys.OZONE_MAX_KEY_LEN,
+        OzoneConfigKeys.OZONE_MAX_KEY_LEN_DEFAULT);
     this.size = this.configuration.getInt(HDDS_KEY_LEN, HDDS_DEFAULT_KEY_LEN);
     this.keyAlgo = this.configuration.get(HDDS_KEY_ALGORITHM,
         HDDS_DEFAULT_KEY_ALGORITHM);
@@ -289,4 +294,8 @@ public class SecurityConfig {
       throw new SecurityException("Unknown security provider:" + provider);
     }
   }
+
+  public int getMaxKeyLength() {
+    return this.getMaxKeyLength;
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSecretManager.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSecretManager.java
new file mode 100644
index 0000000..3b833cb
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSecretManager.java
@@ -0,0 +1,191 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.security;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Map;
+/**
+ * SecretManager for Ozone Master block tokens.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class OzoneBlockTokenSecretManager extends
+    OzoneSecretManager<OzoneBlockTokenIdentifier> {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(OzoneBlockTokenSecretManager.class);;
+  // Will be set by grpc clients for individual datanodes.
+  static final Text SERVICE = new Text("HDDS_SERVICE");
+  private final String omCertSerialId;
+
+  /**
+   * Create a secret manager.
+   *
+   * @param conf
+   * @param blockTokenExpirytime token expiry time for expired tokens in
+   * milliseconds
+   */
+  public OzoneBlockTokenSecretManager(OzoneConfiguration conf,
+      long blockTokenExpirytime, String omCertSerialId) {
+    super(conf, blockTokenExpirytime, blockTokenExpirytime, SERVICE, LOG);
+    this.omCertSerialId = omCertSerialId;
+  }
+
+  @Override
+  public OzoneBlockTokenIdentifier createIdentifier() {
+    throw new SecurityException("Ozone block token can't be created "
+        + "without owner and access mode information.");
+  }
+
+  public OzoneBlockTokenIdentifier createIdentifier(String owner,
+      String blockId, EnumSet<AccessModeProto> modes, long maxLength) {
+    return new OzoneBlockTokenIdentifier(owner, blockId, modes,
+        getTokenExpiryTime(), omCertSerialId, maxLength);
+  }
+
+  /**
+   * Generate an block token for specified user, blockId.
+   *
+   * @param user
+   * @param blockId
+   * @param modes
+   * @param maxLength
+   * @return token
+   */
+  public Token<OzoneBlockTokenIdentifier> generateToken(String user,
+      String blockId, EnumSet<AccessModeProto> modes, long maxLength) {
+    OzoneBlockTokenIdentifier tokenIdentifier = createIdentifier(user,
+        blockId, modes, maxLength);
+    if (LOG.isTraceEnabled()) {
+      long expiryTime = tokenIdentifier.getExpiryDate();
+      String tokenId = tokenIdentifier.toString();
+      LOG.trace("Issued delegation token -> expiryTime:{},tokenId:{}",
+          expiryTime, tokenId);
+    }
+    return new Token<>(tokenIdentifier.getBytes(),
+        createPassword(tokenIdentifier), tokenIdentifier.getKind(), SERVICE);
+  }
+
+  /**
+   * Generate an block token for current user.
+   *
+   * @param blockId
+   * @param modes
+   * @return token
+   */
+  public Token<OzoneBlockTokenIdentifier> generateToken(String blockId,
+      EnumSet<AccessModeProto> modes, long maxLength) throws IOException {
+    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+    String userID = (ugi == null ? null : ugi.getShortUserName());
+    return generateToken(userID, blockId, modes, maxLength);
+  }
+
+  @Override
+  public byte[] retrievePassword(OzoneBlockTokenIdentifier identifier)
+      throws InvalidToken {
+    validateToken(identifier);
+    return createPassword(identifier);
+  }
+
+  @Override
+  public long renewToken(Token<OzoneBlockTokenIdentifier> token,
+      String renewer) throws IOException {
+    throw new UnsupportedOperationException("Renew token operation is not " +
+        "supported for ozone block tokens.");
+  }
+
+  @Override
+  public OzoneBlockTokenIdentifier cancelToken(Token<OzoneBlockTokenIdentifier>
+      token, String canceller) throws IOException {
+    throw new UnsupportedOperationException("Cancel token operation is not " +
+        "supported for ozone block tokens.");
+  }
+
+  /**
+   * Find the OzoneBlockTokenInfo for the given token id, and verify that if the
+   * token is not expired.
+   */
+  public boolean validateToken(OzoneBlockTokenIdentifier identifier)
+      throws InvalidToken {
+    long now = Time.now();
+    if (identifier.getExpiryDate() < now) {
+      throw new InvalidToken("token " + formatTokenId(identifier) + " is " +
+          "expired, current time: " + Time.formatTime(now) +
+          " expiry time: " + identifier.getExpiryDate());
+    }
+
+    if (!verifySignature(identifier, createPassword(identifier))) {
+      throw new InvalidToken("Tampared/Inavalid token.");
+    }
+    return true;
+  }
+
+  /**
+   * Should be called before this object is used.
+   */
+  @Override
+  public synchronized void start(KeyPair keyPair) throws IOException {
+    super.start(keyPair);
+    removeExpiredKeys();
+  }
+
+  /**
+   * Returns expiry time by adding configured expiry time with current time.
+   *
+   * @return Expiry time.
+   */
+  private long getTokenExpiryTime() {
+    return Time.now() + getTokenRenewInterval();
+  }
+
+  /**
+   * Should be called before this object is used.
+   */
+  @Override
+  public synchronized void stop() throws IOException {
+    super.stop();
+  }
+
+  private synchronized void removeExpiredKeys() {
+    // TODO: handle roll private key/certificate
+    long now = Time.now();
+    for (Iterator<Map.Entry<Integer, OzoneSecretKey>> it = allKeys.entrySet()
+        .iterator(); it.hasNext();) {
+      Map.Entry<Integer, OzoneSecretKey> e = it.next();
+      OzoneSecretKey key = e.getValue();
+      if (key.getExpiryDate() < now) {
+        it.remove();
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java
new file mode 100644
index 0000000..1b9414b
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java
@@ -0,0 +1,455 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.security;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.ozone.security.OzoneSecretStore.OzoneManagerSecretState;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier.TokenInfo;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.HadoopKerberosName;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.Daemon;
+import org.apache.hadoop.util.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * SecretManager for Ozone Master. Responsible for signing identifiers with
+ * private key,
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class OzoneDelegationTokenSecretManager<T extends OzoneTokenIdentifier>
+    extends OzoneSecretManager<T> {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(OzoneDelegationTokenSecretManager.class);
+  private final Map<T, TokenInfo> currentTokens;
+  private final OzoneSecretStore store;
+  private Thread tokenRemoverThread;
+  private final long tokenRemoverScanInterval;
+  /**
+   * If the delegation token update thread holds this lock, it will not get
+   * interrupted.
+   */
+  private Object noInterruptsLock = new Object();
+
+  /**
+   * Create a secret manager.
+   *
+   * @param conf configuration.
+   * @param tokenMaxLifetime the maximum lifetime of the delegation tokens in
+   * milliseconds
+   * @param tokenRenewInterval how often the tokens must be renewed in
+   * milliseconds
+   * @param dtRemoverScanInterval how often the tokens are scanned for expired
+   * tokens in milliseconds
+   */
+  public OzoneDelegationTokenSecretManager(OzoneConfiguration conf,
+      long tokenMaxLifetime, long tokenRenewInterval,
+      long dtRemoverScanInterval, Text service) throws IOException {
+    super(conf, tokenMaxLifetime, tokenRenewInterval, service, LOG);
+    currentTokens = new ConcurrentHashMap();
+    this.tokenRemoverScanInterval = dtRemoverScanInterval;
+    this.store = new OzoneSecretStore(conf);
+    loadTokenSecretState(store.loadState());
+  }
+
+  @Override
+  public T createIdentifier() {
+    return (T) T.newInstance();
+  }
+
+  /**
+   * Create new Identifier with given,owner,renwer and realUser.
+   *
+   * @return T
+   */
+  public T createIdentifier(Text owner, Text renewer, Text realUser) {
+    return (T) T.newInstance(owner, renewer, realUser);
+  }
+
+  /**
+   * Returns {@link Token} for given identifier.
+   *
+   * @param owner
+   * @param renewer
+   * @param realUser
+   * @return Token
+   * @throws IOException to allow future exceptions to be added without breaking
+   *                     compatibility
+   */
+  public Token<T> createToken(Text owner, Text renewer, Text realUser)
+      throws IOException {
+    T identifier = createIdentifier(owner, renewer, realUser);
+    updateIdentifierDetails(identifier);
+
+    byte[] password = createPassword(identifier.getBytes(),
+        getCurrentKey().getPrivateKey());
+    addToTokenStore(identifier, password);
+    Token<T> token = new Token<>(identifier.getBytes(), password,
+        identifier.getKind(), getService());
+    if (LOG.isTraceEnabled()) {
+      long expiryTime = identifier.getIssueDate() + getTokenRenewInterval();
+      String tokenId = identifier.toStringStable();
+      LOG.trace("Issued delegation token -> expiryTime:{},tokenId:{}",
+          expiryTime, tokenId);
+    }
+    return token;
+  }
+
+  /**
+   * Stores given identifier in token store.
+   *
+   * @param identifier
+   * @param password
+   * @throws IOException
+   */
+  private void addToTokenStore(T identifier, byte[] password)
+      throws IOException {
+    TokenInfo tokenInfo = new TokenInfo(identifier.getIssueDate()
+        + getTokenRenewInterval(), password, identifier.getTrackingId());
+    currentTokens.put(identifier, tokenInfo);
+    store.storeToken(identifier, tokenInfo.getRenewDate());
+  }
+
+  /**
+   * Updates issue date, master key id and sequence number for identifier.
+   *
+   * @param identifier the identifier to validate
+   */
+  private void updateIdentifierDetails(T identifier) {
+    int sequenceNum;
+    long now = Time.monotonicNow();
+    sequenceNum = incrementDelegationTokenSeqNum();
+    identifier.setIssueDate(now);
+    identifier.setMasterKeyId(getCurrentKey().getKeyId());
+    identifier.setSequenceNumber(sequenceNum);
+    identifier.setMaxDate(Time.monotonicNow() + getTokenMaxLifetime());
+  }
+
+  /**
+   * Renew a delegation token.
+   *
+   * @param token the token to renew
+   * @param renewer the full principal name of the user doing the renewal
+   * @return the new expiration time
+   * @throws InvalidToken           if the token is invalid
+   * @throws AccessControlException if the user can't renew token
+   */
+  @Override
+  public synchronized long renewToken(Token<T> token, String renewer)
+      throws IOException {
+    ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier());
+    DataInputStream in = new DataInputStream(buf);
+    T id = (T) T.readProtoBuf(in);
+    if(LOG.isDebugEnabled()) {
+      LOG.debug("Token renewal for identifier: {}, total currentTokens: {}",
+          formatTokenId(id), currentTokens.size());
+    }
+
+    long now = Time.monotonicNow();
+    if (id.getMaxDate() < now) {
+      throw new InvalidToken(renewer + " tried to renew an expired token "
+          + formatTokenId(id) + " max expiration date: "
+          + Time.formatTime(id.getMaxDate())
+          + " currentTime: " + Time.formatTime(now));
+    }
+    validateToken(id);
+    if ((id.getRenewer() == null) || (id.getRenewer().toString().isEmpty())) {
+      throw new AccessControlException(renewer +
+          " tried to renew a token " + formatTokenId(id)
+          + " without a renewer");
+    }
+    if (!id.getRenewer().toString().equals(renewer)) {
+      throw new AccessControlException(renewer
+          + " tries to renew a token " + formatTokenId(id)
+          + " with non-matching renewer " + id.getRenewer());
+    }
+    OzoneSecretKey key = allKeys.get(id.getMasterKeyId());
+    if (key == null) {
+      throw new InvalidToken("Unable to find master key for keyId="
+          + id.getMasterKeyId()
+          + " from cache. Failed to renew an unexpired token "
+          + formatTokenId(id) + " with sequenceNumber="
+          + id.getSequenceNumber());
+    }
+    byte[] password = createPassword(token.getIdentifier(),
+        key.getPrivateKey());
+
+    long renewTime = Math.min(id.getMaxDate(), now + getTokenRenewInterval());
+    try {
+      addToTokenStore(id, password);
+    } catch (IOException e) {
+      LOG.error("Unable to update token " + id.getSequenceNumber(), e);
+    }
+    return renewTime;
+  }
+
+  /**
+   * Cancel a token by removing it from store and cache.
+   *
+   * @return Identifier of the canceled token
+   * @throws InvalidToken           for invalid token
+   * @throws AccessControlException if the user isn't allowed to cancel
+   */
+  public T cancelToken(Token<T> token, String canceller) throws IOException {
+    T id = (T) T.readProtoBuf(token.getIdentifier());
+    LOG.debug("Token cancellation requested for identifier: {}",
+        formatTokenId(id));
+
+    if (id.getUser() == null) {
+      throw new InvalidToken("Token with no owner " + formatTokenId(id));
+    }
+    String owner = id.getUser().getUserName();
+    Text renewer = id.getRenewer();
+    HadoopKerberosName cancelerKrbName = new HadoopKerberosName(canceller);
+    String cancelerShortName = cancelerKrbName.getShortName();
+    if (!canceller.equals(owner)
+        && (renewer == null || renewer.toString().isEmpty()
+        || !cancelerShortName
+        .equals(renewer.toString()))) {
+      throw new AccessControlException(canceller
+          + " is not authorized to cancel the token " + formatTokenId(id));
+    }
+    try {
+      store.removeToken(id);
+    } catch (IOException e) {
+      LOG.error("Unable to remove token " + id.getSequenceNumber(), e);
+    }
+    TokenInfo info = currentTokens.remove(id);
+    if (info == null) {
+      throw new InvalidToken("Token not found " + formatTokenId(id));
+    }
+    return id;
+  }
+
+  @Override
+  public byte[] retrievePassword(T identifier) throws InvalidToken {
+    return validateToken(identifier).getPassword();
+  }
+
+  /**
+   * Checks if TokenInfo for the given identifier exists in database and if the
+   * token is expired.
+   */
+  public TokenInfo validateToken(T identifier) throws InvalidToken {
+    TokenInfo info = currentTokens.get(identifier);
+    if (info == null) {
+      throw new InvalidToken("token " + formatTokenId(identifier)
+          + " can't be found in cache");
+    }
+    long now = Time.monotonicNow();
+    if (info.getRenewDate() < now) {
+      throw new InvalidToken("token " + formatTokenId(identifier) + " is " +
+          "expired, current time: " + Time.formatTime(now) +
+          " expected renewal time: " + Time.formatTime(info.getRenewDate()));
+    }
+    if (!verifySignature(identifier, info.getPassword())) {
+      throw new InvalidToken("Tampared/Inavalid token.");
+    }
+    return info;
+  }
+
+  // TODO: handle roll private key/certificate
+  private synchronized void removeExpiredKeys() {
+    long now = Time.monotonicNow();
+    for (Iterator<Map.Entry<Integer, OzoneSecretKey>> it = allKeys.entrySet()
+        .iterator(); it.hasNext();) {
+      Map.Entry<Integer, OzoneSecretKey> e = it.next();
+      OzoneSecretKey key = e.getValue();
+      if (key.getExpiryDate() < now && key.getExpiryDate() != -1) {
+        if (!key.equals(getCurrentKey())) {
+          it.remove();
+          try {
+            store.removeTokenMasterKey(key);
+          } catch (IOException ex) {
+            LOG.error("Unable to remove master key " + key.getKeyId(), ex);
+          }
+        }
+      }
+    }
+  }
+
+  private void loadTokenSecretState(OzoneManagerSecretState<T> state)
+      throws IOException {
+    LOG.info("Loading token state into token manager.");
+    for (OzoneSecretKey key : state.ozoneManagerSecretState()) {
+      allKeys.putIfAbsent(key.getKeyId(), key);
+      incrementCurrentKeyId();
+    }
+    for (Map.Entry<T, Long> entry : state.getTokenState().entrySet()) {
+      addPersistedDelegationToken(entry.getKey(), entry.getValue());
+    }
+  }
+
+  private void addPersistedDelegationToken(
+      T identifier, long renewDate)
+      throws IOException {
+    if (isRunning()) {
+      // a safety check
+      throw new IOException(
+          "Can't add persisted delegation token to a running SecretManager.");
+    }
+    int keyId = identifier.getMasterKeyId();
+    OzoneSecretKey dKey = allKeys.get(keyId);
+    if (dKey == null) {
+      LOG.warn("No KEY found for persisted identifier "
+          + formatTokenId(identifier));
+      return;
+    }
+
+    PrivateKey privateKey = dKey.getPrivateKey();
+    byte[] password = createPassword(identifier.getBytes(), privateKey);
+    if (identifier.getSequenceNumber() > getDelegationTokenSeqNum()) {
+      setDelegationTokenSeqNum(identifier.getSequenceNumber());
+    }
+    if (currentTokens.get(identifier) == null) {
+      currentTokens.put(identifier, new TokenInfo(renewDate,
+          password, identifier.getTrackingId()));
+    } else {
+      throw new IOException("Same delegation token being added twice: "
+          + formatTokenId(identifier));
+    }
+  }
+
+  /**
+   * Should be called before this object is used.
+   */
+  @Override
+  public synchronized void start(KeyPair keyPair) throws IOException {
+    super.start(keyPair);
+    storeKey(getCurrentKey());
+    removeExpiredKeys();
+    tokenRemoverThread = new Daemon(new ExpiredTokenRemover());
+    tokenRemoverThread.start();
+  }
+
+  private void storeKey(OzoneSecretKey key) throws IOException {
+    store.storeTokenMasterKey(key);
+    if (!allKeys.containsKey(key.getKeyId())) {
+      allKeys.put(key.getKeyId(), key);
+    }
+  }
+
+  public void stopThreads() {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Stopping expired delegation token remover thread");
+    }
+    setIsRunning(false);
+
+    if (tokenRemoverThread != null) {
+      synchronized (noInterruptsLock) {
+        tokenRemoverThread.interrupt();
+      }
+      try {
+        tokenRemoverThread.join();
+      } catch (InterruptedException e) {
+        throw new RuntimeException(
+            "Unable to join on token removal thread", e);
+      }
+    }
+  }
+
+  /**
+   * Stops the OzoneDelegationTokenSecretManager.
+   *
+   * @throws IOException
+   */
+  @Override
+  public void stop() throws IOException {
+    super.stop();
+    stopThreads();
+    if (this.store != null) {
+      this.store.close();
+    }
+  }
+
+  /**
+   * Remove expired delegation tokens from cache and persisted store.
+   */
+  private void removeExpiredToken()  {
+    long now = Time.monotonicNow();
+    synchronized (this) {
+      Iterator<Map.Entry<T,
+          TokenInfo>> i = currentTokens.entrySet().iterator();
+      while (i.hasNext()) {
+        Map.Entry<T,
+            TokenInfo> entry = i.next();
+        long renewDate = entry.getValue().getRenewDate();
+        if (renewDate < now) {
+          i.remove();
+          try {
+            store.removeToken(entry.getKey());
+          } catch (IOException e) {
+            if(LOG.isDebugEnabled()) {
+              LOG.debug("Failed to remove expired token {}", entry.getValue());
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private class ExpiredTokenRemover extends Thread {
+    private long lastTokenCacheCleanup;
+
+    @Override
+    public void run() {
+      LOG.info("Starting expired delegation token remover thread, "
+          + "tokenRemoverScanInterval=" + getTokenRemoverScanInterval()
+          / (60 * 1000) + " min(s)");
+      try {
+        while (isRunning()) {
+          long now = Time.monotonicNow();
+          if (lastTokenCacheCleanup + getTokenRemoverScanInterval()
+              < now) {
+            removeExpiredToken();
+            lastTokenCacheCleanup = now;
+          }
+          try {
+            Thread.sleep(Math.min(5000,
+                getTokenRemoverScanInterval())); // 5 seconds
+          } catch (InterruptedException ie) {
+            LOG.error("ExpiredTokenRemover received " + ie);
+          }
+        }
+      } catch (Throwable t) {
+        LOG.error("ExpiredTokenRemover thread received unexpected exception",
+            t);
+        Runtime.getRuntime().exit(-1);
+      }
+    }
+  }
+
+  public long getTokenRemoverScanInterval() {
+    return tokenRemoverScanInterval;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
index 0c84404..01ef8bb 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
@@ -18,8 +18,17 @@
 package org.apache.hadoop.ozone.security;
 
 import com.google.common.base.Preconditions;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.token.SecretManager;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.security.token.TokenIdentifier;
+import org.slf4j.Logger;
+
 import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
@@ -27,25 +36,9 @@ import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Signature;
 import java.security.SignatureException;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.hadoop.classification.InterfaceAudience;
-import org.apache.hadoop.classification.InterfaceStability;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.io.Text;
-import org.apache.hadoop.ozone.OzoneConfigKeys;
-import org.apache.hadoop.ozone.security.OzoneSecretStore.OzoneManagerSecretState;
-import org.apache.hadoop.ozone.security.OzoneTokenIdentifier.TokenInfo;
-import org.apache.hadoop.security.AccessControlException;
-import org.apache.hadoop.security.HadoopKerberosName;
-import org.apache.hadoop.security.token.SecretManager;
-import org.apache.hadoop.security.token.Token;
-import org.apache.hadoop.util.Daemon;
-import org.apache.hadoop.util.Time;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * SecretManager for Ozone Master. Responsible for signing identifiers with
@@ -53,33 +46,23 @@ import org.slf4j.LoggerFactory;
  */
 @InterfaceAudience.Private
 @InterfaceStability.Unstable
-public class OzoneSecretManager<T extends OzoneTokenIdentifier>
+public abstract class OzoneSecretManager<T extends TokenIdentifier>
     extends SecretManager<T> {
 
-  private static final Logger LOG = LoggerFactory
-      .getLogger(OzoneSecretManager.class);
+  private final Logger logger;
   /**
    * The name of the Private/Public Key based hashing algorithm.
    */
-  private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA";
+  private final SecurityConfig securityConfig;
   private final long tokenMaxLifetime;
   private final long tokenRenewInterval;
-  private final long tokenRemoverScanInterval;
   private final Text service;
-  private final Map<Integer, OzoneSecretKey> allKeys;
-  private final Map<T, TokenInfo> currentTokens;
-  private final OzoneSecretStore store;
-  private Thread tokenRemoverThread;
   private volatile boolean running;
-  private AtomicInteger tokenSequenceNumber;
   private OzoneSecretKey currentKey;
-  private AtomicInteger currentKeyId;
-  /**
-   * If the delegation token update thread holds this lock, it will not get
-   * interrupted.
-   */
-  private Object noInterruptsLock = new Object();
   private int maxKeyLength;
+  private AtomicInteger currentKeyId;
+  private AtomicInteger tokenSequenceNumber;
+  protected final Map<Integer, OzoneSecretKey> allKeys;
 
   /**
    * Create a secret manager.
@@ -89,100 +72,21 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
    * milliseconds
    * @param tokenRenewInterval how often the tokens must be renewed in
    * milliseconds
-   * @param dtRemoverScanInterval how often the tokens are scanned for expired
-   * tokens in milliseconds
+   * @param service name of service
    */
   public OzoneSecretManager(OzoneConfiguration conf, long tokenMaxLifetime,
-      long tokenRenewInterval, long dtRemoverScanInterval, Text service)
-      throws IOException {
+      long tokenRenewInterval, Text service, Logger logger) {
+    this.securityConfig = new SecurityConfig(conf);
     this.tokenMaxLifetime = tokenMaxLifetime;
     this.tokenRenewInterval = tokenRenewInterval;
-    this.tokenRemoverScanInterval = dtRemoverScanInterval;
-
-    currentTokens = new ConcurrentHashMap();
-    allKeys = new ConcurrentHashMap<>();
     currentKeyId = new AtomicInteger();
     tokenSequenceNumber = new AtomicInteger();
-    this.store = new OzoneSecretStore(conf);
-    loadTokenSecretState(store.loadState());
+    allKeys = new ConcurrentHashMap<>();
     this.service = service;
-    this.maxKeyLength = conf.getInt(OzoneConfigKeys.OZONE_MAX_KEY_LEN,
-        OzoneConfigKeys.OZONE_MAX_KEY_LEN_DEFAULT);
-  }
-
-  @Override
-  public T createIdentifier() {
-    return (T) T.newInstance();
+    this.maxKeyLength = securityConfig.getMaxKeyLength();
+    this.logger = logger;
   }
 
-  /**
-   * Create new Identifier with given,owner,renwer and realUser.
-   *
-   * @return T
-   */
-  public T createIdentifier(Text owner, Text renewer, Text realUser) {
-    return (T) T.newInstance(owner, renewer, realUser);
-  }
-
-  /**
-   * Returns {@link Token} for given identifier.
-   *
-   * @param owner
-   * @param renewer
-   * @param realUser
-   * @return Token
-   * @throws IOException to allow future exceptions to be added without breaking
-   *                     compatibility
-   */
-  public Token<T> createToken(Text owner, Text renewer, Text realUser)
-      throws IOException {
-    T identifier = createIdentifier(owner, renewer, realUser);
-    updateIdentifierDetails(identifier);
-
-    byte[] password = createPassword(identifier.getBytes(),
-        currentKey.getPrivateKey());
-    addToTokenStore(identifier, password);
-    Token<T> token = new Token<>(identifier.getBytes(), password,
-        identifier.getKind(), service);
-    if (LOG.isTraceEnabled()) {
-      long expiryTime = identifier.getIssueDate() + tokenRenewInterval;
-      String tokenId = identifier.toStringStable();
-      LOG.trace("Issued delegation token -> expiryTime:{},tokenId:{}",
-          expiryTime, tokenId);
-    }
-
-    return token;
-  }
-
-  /**
-   * Stores given identifier in token store.
-   *
-   * @param identifier
-   * @param password
-   * @throws IOException
-   */
-  private void addToTokenStore(T identifier, byte[] password)
-      throws IOException {
-    TokenInfo tokenInfo = new TokenInfo(identifier.getIssueDate()
-        + tokenRenewInterval, password, identifier.getTrackingId());
-    currentTokens.put(identifier, tokenInfo);
-    store.storeToken(identifier, tokenInfo.getRenewDate());
-  }
-
-  /**
-   * Updates issue date, master key id and sequence number for identifier.
-   *
-   * @param identifier the identifier to validate
-   */
-  private void updateIdentifierDetails(T identifier) {
-    int sequenceNum;
-    long now = Time.monotonicNow();
-    sequenceNum = incrementDelegationTokenSeqNum();
-    identifier.setIssueDate(now);
-    identifier.setMasterKeyId(currentKey.getKeyId());
-    identifier.setSequenceNumber(sequenceNum);
-    identifier.setMaxDate(Time.monotonicNow() + tokenMaxLifetime);
-  }
 
   /**
    * Compute HMAC of the identifier using the private key and return the output
@@ -196,7 +100,7 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
       throws OzoneSecurityException {
     try {
       Signature rsaSignature = Signature.getInstance(
-          DEFAULT_SIGNATURE_ALGORITHM);
+          getDefaultSignatureAlgorithm());
       rsaSignature.initSign(privateKey);
       rsaSignature.update(identifier);
       return rsaSignature.sign();
@@ -210,22 +114,31 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
 
   @Override
   public byte[] createPassword(T identifier) {
-    LOG.debug("Creating password for identifier: {}, currentKey: {}",
+    logger.debug("Creating password for identifier: {}, currentKey: {}",
         formatTokenId(identifier), currentKey.getKeyId());
     byte[] password = null;
     try {
       password = createPassword(identifier.getBytes(),
           currentKey.getPrivateKey());
     } catch (IOException ioe) {
-      LOG.error("Could not store token {}!!", formatTokenId(identifier),
+      logger.error("Could not store token {}!!", formatTokenId(identifier),
           ioe);
     }
     return password;
   }
 
+  /**
+   * Default implementation for Ozone. Verifies if hash in token is legit.
+   * */
   @Override
   public byte[] retrievePassword(T identifier) throws InvalidToken {
-    return checkToken(identifier).getPassword();
+    byte[] password = createPassword(identifier);
+    // TODO: Revisit this when key/certificate rotation is implemented.
+    // i.e Try all valid keys instead of current key only.
+    if (!verifySignature(identifier, password)) {
+      throw new InvalidToken("Tampared/Inavalid token.");
+    }
+    return password;
   }
 
   /**
@@ -237,52 +150,8 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
    * @throws InvalidToken           if the token is invalid
    * @throws AccessControlException if the user can't renew token
    */
-  public synchronized long renewToken(Token<T> token, String renewer)
-      throws IOException {
-    ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier());
-    DataInputStream in = new DataInputStream(buf);
-    T id = (T) T.readProtoBuf(in);
-    LOG.debug("Token renewal for identifier: {}, total currentTokens: {}",
-        formatTokenId(id), currentTokens.size());
-
-    long now = Time.monotonicNow();
-    if (id.getMaxDate() < now) {
-      throw new InvalidToken(renewer + " tried to renew an expired token "
-          + formatTokenId(id) + " max expiration date: "
-          + Time.formatTime(id.getMaxDate())
-          + " currentTime: " + Time.formatTime(now));
-    }
-    checkToken(id);
-    if ((id.getRenewer() == null) || (id.getRenewer().toString().isEmpty())) {
-      throw new AccessControlException(renewer +
-          " tried to renew a token " + formatTokenId(id)
-          + " without a renewer");
-    }
-    if (!id.getRenewer().toString().equals(renewer)) {
-      throw new AccessControlException(renewer
-          + " tries to renew a token " + formatTokenId(id)
-          + " with non-matching renewer " + id.getRenewer());
-    }
-    OzoneSecretKey key = allKeys.get(id.getMasterKeyId());
-    if (key == null) {
-      throw new InvalidToken("Unable to find master key for keyId="
-          + id.getMasterKeyId()
-          + " from cache. Failed to renew an unexpired token "
-          + formatTokenId(id) + " with sequenceNumber="
-          + id.getSequenceNumber());
-    }
-    byte[] password = createPassword(token.getIdentifier(),
-        key.getPrivateKey());
-
-    long renewTime = Math.min(id.getMaxDate(), now + tokenRenewInterval);
-    try {
-      addToTokenStore(id, password);
-    } catch (IOException e) {
-      LOG.error("Unable to update token " + id.getSequenceNumber(), e);
-    }
-    return renewTime;
-  }
-
+  public abstract long renewToken(Token<T> token, String renewer)
+      throws IOException;
   /**
    * Cancel a token by removing it from store and cache.
    *
@@ -290,44 +159,8 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
    * @throws InvalidToken           for invalid token
    * @throws AccessControlException if the user isn't allowed to cancel
    */
-  public T cancelToken(Token<T> token, String canceller) throws IOException {
-    T id = (T) T.readProtoBuf(token.getIdentifier());
-    LOG.debug("Token cancellation requested for identifier: {}",
-        formatTokenId(id));
-
-    if (id.getUser() == null) {
-      throw new InvalidToken("Token with no owner " + formatTokenId(id));
-    }
-    String owner = id.getUser().getUserName();
-    Text renewer = id.getRenewer();
-    HadoopKerberosName cancelerKrbName = new HadoopKerberosName(canceller);
-    String cancelerShortName = cancelerKrbName.getShortName();
-    if (!canceller.equals(owner)
-        && (renewer == null || renewer.toString().isEmpty()
-        || !cancelerShortName
-        .equals(renewer.toString()))) {
-      throw new AccessControlException(canceller
-          + " is not authorized to cancel the token " + formatTokenId(id));
-    }
-    try {
-      store.removeToken(id);
-    } catch (IOException e) {
-      LOG.error("Unable to remove token " + id.getSequenceNumber(), e);
-    }
-    TokenInfo info = currentTokens.remove(id);
-    if (info == null) {
-      throw new InvalidToken("Token not found " + formatTokenId(id));
-    }
-    return id;
-  }
-
-  public int getCurrentKeyId() {
-    return currentKeyId.get();
-  }
-
-  public void setCurrentKeyId(int keyId) {
-    currentKeyId.set(keyId);
-  }
+  public abstract T cancelToken(Token<T> token, String canceller)
+      throws IOException;
 
   public int incrementCurrentKeyId() {
     return currentKeyId.incrementAndGet();
@@ -346,14 +179,31 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
   }
 
   /**
-   * Validates if given token is valid.
+   * Update the current master key. This is called once by start method before
+   * tokenRemoverThread is created,
+   */
+  private OzoneSecretKey updateCurrentKey(KeyPair keyPair) throws IOException {
+    logger.info("Updating the current master key for generating tokens");
+
+    // TODO: fix me based on the certificate expire time to set the key
+    // expire time.
+    int newCurrentId = incrementCurrentKeyId();
+    OzoneSecretKey newKey = new OzoneSecretKey(newCurrentId, -1,
+        keyPair, maxKeyLength);
+    currentKey = newKey;
+    return currentKey;
+  }
+
+  /**
+   * Validates if given hash is valid.
    *
    * @param identifier
    * @param password
    */
-  private boolean validateToken(T identifier, byte[] password) {
+  public boolean verifySignature(T identifier, byte[] password) {
     try {
-      Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+      Signature rsaSignature =
+          Signature.getInstance(getDefaultSignatureAlgorithm());
       rsaSignature.initVerify(currentKey.getPublicKey());
       rsaSignature.update(identifier.getBytes());
       return rsaSignature.verify(password);
@@ -363,179 +213,45 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
     }
   }
 
-  /**
-   * Checks if TokenInfo for the given identifier exists in database and if the
-   * token is expired.
-   */
-  public TokenInfo checkToken(T identifier) throws InvalidToken {
-    TokenInfo info = currentTokens.get(identifier);
-    if (info == null) {
-      throw new InvalidToken("token " + formatTokenId(identifier)
-          + " can't be found in cache");
-    }
-    long now = Time.monotonicNow();
-    if (info.getRenewDate() < now) {
-      throw new InvalidToken("token " + formatTokenId(identifier) + " is " +
-          "expired, current time: " + Time.formatTime(now) +
-          " expected renewal time: " + Time.formatTime(info.getRenewDate()));
-    }
-    if (!validateToken(identifier, info.getPassword())) {
-      throw new InvalidToken("Tampared/Inavalid token.");
-    }
-    return info;
-  }
-
-  // TODO: handle roll private key/certificate
-  private synchronized void removeExpiredKeys() {
-    long now = Time.monotonicNow();
-    for (Iterator<Map.Entry<Integer, OzoneSecretKey>> it = allKeys.entrySet()
-        .iterator(); it.hasNext();) {
-      Map.Entry<Integer, OzoneSecretKey> e = it.next();
-      OzoneSecretKey key = e.getValue();
-      if (key.getExpiryDate() < now && key.getExpiryDate() != -1) {
-        if (!key.equals(currentKey)) {
-          it.remove();
-          try {
-            store.removeTokenMasterKey(key);
-          } catch (IOException ex) {
-            LOG.error("Unable to remove master key " + key.getKeyId(), ex);
-          }
-        }
-      }
-    }
-  }
-
-  private void loadTokenSecretState(OzoneManagerSecretState<T> state)
-      throws IOException {
-    LOG.info("Loading token state into token manager.");
-    for (OzoneSecretKey key : state.ozoneManagerSecretState()) {
-      allKeys.putIfAbsent(key.getKeyId(), key);
-    }
-    for (Map.Entry<T, Long> entry : state.getTokenState().entrySet()) {
-      addPersistedDelegationToken(entry.getKey(), entry.getValue());
-    }
-  }
-
-  private String formatTokenId(T id) {
+  public String formatTokenId(T id) {
     return "(" + id + ")";
   }
 
-  private void addPersistedDelegationToken(
-      T identifier, long renewDate)
-      throws IOException {
-    if (running) {
-      // a safety check
-      throw new IOException(
-          "Can't add persisted delegation token to a running SecretManager.");
-    }
-    int keyId = identifier.getMasterKeyId();
-    OzoneSecretKey dKey = allKeys.get(keyId);
-    if (dKey == null) {
-      LOG.warn("No KEY found for persisted identifier "
-          + formatTokenId(identifier));
-      return;
-    }
-
-    PrivateKey privateKey = dKey.getPrivateKey();
-    byte[] password = createPassword(identifier.getBytes(), privateKey);
-    if (identifier.getSequenceNumber() > getDelegationTokenSeqNum()) {
-      setDelegationTokenSeqNum(identifier.getSequenceNumber());
-    }
-    if (currentTokens.get(identifier) == null) {
-      currentTokens.put(identifier, new TokenInfo(renewDate,
-          password, identifier.getTrackingId()));
-    } else {
-      throw new IOException("Same delegation token being added twice: "
-          + formatTokenId(identifier));
-    }
-  }
-
   /**
    * Should be called before this object is used.
+   *
+   * @param keyPair
+   * @throws IOException
    */
-  public void startThreads(KeyPair keyPair) throws IOException {
-    Preconditions.checkState(!running);
+  public synchronized void start(KeyPair keyPair) throws IOException {
+    Preconditions.checkState(!isRunning());
     updateCurrentKey(keyPair);
-    removeExpiredKeys();
-    synchronized (this) {
-      running = true;
-      tokenRemoverThread = new Daemon(new ExpiredTokenRemover());
-      tokenRemoverThread.start();
-    }
-  }
-
-  public void stopThreads() {
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Stopping expired delegation token remover thread");
-    }
-    running = false;
-
-    if (tokenRemoverThread != null) {
-      synchronized (noInterruptsLock) {
-        tokenRemoverThread.interrupt();
-      }
-      try {
-        tokenRemoverThread.join();
-      } catch (InterruptedException e) {
-        throw new RuntimeException(
-            "Unable to join on token removal thread", e);
-      }
-    }
+    setIsRunning(true);
   }
 
   /**
-   * Stops the OzoneSecretManager.
+   * Stops the OzoneDelegationTokenSecretManager.
    *
    * @throws IOException
    */
-  public void stop() throws IOException {
-    stopThreads();
-    if (this.store != null) {
-      this.store.close();
-    }
+  public synchronized void stop() throws IOException {
+    setIsRunning(false);
   }
 
-  /**
-   * Update the current master key. This is called once by startThreads before
-   * tokenRemoverThread is created,
-   */
-  private void updateCurrentKey(KeyPair keyPair) throws IOException {
-    LOG.info("Updating the current master key for generating tokens");
-
-    // TODO: fix me based on the certificate expire time to set the key
-    // expire time.
-    int newCurrentId = incrementCurrentKeyId();
-    OzoneSecretKey newKey = new OzoneSecretKey(newCurrentId, -1,
-        keyPair, maxKeyLength);
+  public String getDefaultSignatureAlgorithm() {
+    return securityConfig.getSignatureAlgo();
+  }
 
-    store.storeTokenMasterKey(newKey);
-    if (!allKeys.containsKey(newKey.getKeyId())) {
-      allKeys.put(newKey.getKeyId(), newKey);
-    }
+  public long getTokenMaxLifetime() {
+    return tokenMaxLifetime;
+  }
 
-    synchronized (this) {
-      currentKey = newKey;
-    }
+  public long getTokenRenewInterval() {
+    return tokenRenewInterval;
   }
 
-  /**
-   * Remove expired delegation tokens from cache and persisted store.
-   */
-  private void removeExpiredToken() throws IOException {
-    long now = Time.monotonicNow();
-    synchronized (this) {
-      Iterator<Map.Entry<T,
-          TokenInfo>> i = currentTokens.entrySet().iterator();
-      while (i.hasNext()) {
-        Map.Entry<T,
-            TokenInfo> entry = i.next();
-        long renewDate = entry.getValue().getRenewDate();
-        if (renewDate < now) {
-          i.remove();
-          store.removeToken(entry.getKey());
-        }
-      }
-    }
+  public Text getService() {
+    return service;
   }
 
   /**
@@ -547,52 +263,20 @@ public class OzoneSecretManager<T extends OzoneTokenIdentifier>
     return running;
   }
 
-  /**
-   * Returns expiry time of a token given its identifier.
-   *
-   * @param dtId DelegationTokenIdentifier of a token
-   * @return Expiry time of the token
-   * @throws IOException
-   */
-  public long getTokenExpiryTime(T dtId)
-      throws IOException {
-    TokenInfo info = currentTokens.get(dtId);
-    if (info != null) {
-      return info.getRenewDate();
-    } else {
-      throw new IOException("No delegation token found for this identifier");
-    }
+  public void setIsRunning(boolean val) {
+    running = val;
   }
 
-  private class ExpiredTokenRemover extends Thread {
-    private long lastTokenCacheCleanup;
+  public OzoneSecretKey getCurrentKey() {
+    return currentKey;
+  }
 
-    @Override
-    public void run() {
-      LOG.info("Starting expired delegation token remover thread, "
-          + "tokenRemoverScanInterval=" + tokenRemoverScanInterval
-          / (60 * 1000) + " min(s)");
-      try {
-        while (running) {
-          long now = Time.monotonicNow();
-          if (lastTokenCacheCleanup + tokenRemoverScanInterval
-              < now) {
-            removeExpiredToken();
-            lastTokenCacheCleanup = now;
-          }
-          try {
-            Thread.sleep(Math.min(5000,
-                tokenRemoverScanInterval)); // 5 seconds
-          } catch (InterruptedException ie) {
-            LOG.error("ExpiredTokenRemover received " + ie);
-          }
-        }
-      } catch (Throwable t) {
-        LOG.error("ExpiredTokenRemover thread received unexpected exception",
-            t);
-        Runtime.getRuntime().exit(-1);
-      }
-    }
+  public AtomicInteger getCurrentKeyId() {
+    return currentKeyId;
+  }
+
+  public AtomicInteger getTokenSequenceNumber() {
+    return tokenSequenceNumber;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
new file mode 100644
index 0000000..4692266
--- /dev/null
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneBlockTokenSecretManager.java
@@ -0,0 +1,146 @@
+/*
+ * 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.ozone.security;
+
+import org.apache.hadoop.hdds.HddsConfigKeys;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto;
+import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.test.LambdaTestUtils;
+import org.apache.hadoop.util.Time;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.security.KeyPair;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.EnumSet;
+
+/**
+ * Test class for {@link OzoneBlockTokenSecretManager}.
+ */
+public class TestOzoneBlockTokenSecretManager {
+
+  private OzoneBlockTokenSecretManager secretManager;
+  private KeyPair keyPair;
+  private X509Certificate x509Certificate;
+  private long expiryTime;
+  private String omCertSerialId;
+  private static final String BASEDIR = GenericTestUtils
+      .getTempPath(TestOzoneBlockTokenSecretManager.class.getSimpleName());
+
+
+  @Before
+  public void setUp() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, BASEDIR);
+    // Create Ozone Master key pair.
+    keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
+    expiryTime = Time.monotonicNow() + 60 * 60 * 24;
+    // Create Ozone Master certificate (SCM CA issued cert) and key store.
+    x509Certificate = KeyStoreTestUtil
+        .generateCertificate("CN=OzoneMaster", keyPair, 30, "SHA256withRSA");
+    omCertSerialId = x509Certificate.getSerialNumber().toString();
+    secretManager = new OzoneBlockTokenSecretManager(conf,
+        expiryTime, omCertSerialId);
+    secretManager.start(keyPair);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    secretManager = null;
+  }
+
+  @Test
+  public void testGenerateToken() throws Exception {
+    Token<OzoneBlockTokenIdentifier> token = secretManager.generateToken(
+        "101", EnumSet.allOf(AccessModeProto.class), 100);
+    OzoneBlockTokenIdentifier identifier =
+        OzoneBlockTokenIdentifier.readFieldsProtobuf(new DataInputStream(
+            new ByteArrayInputStream(token.getIdentifier())));
+    // Check basic details.
+    Assert.assertTrue(identifier.getBlockId().equals("101"));
+    Assert.assertTrue(identifier.getAccessModes().equals(EnumSet
+        .allOf(AccessModeProto.class)));
+    Assert.assertTrue(identifier.getOmCertSerialId().equals(omCertSerialId));
+
+    validateHash(token.getPassword(), token.getIdentifier());
+  }
+
+  @Test
+  public void testCreateIdentifierSuccess() throws Exception {
+    OzoneBlockTokenIdentifier btIdentifier = secretManager.createIdentifier(
+        "testUser", "101", EnumSet.allOf(AccessModeProto.class), 100);
+
+    // Check basic details.
+    Assert.assertTrue(btIdentifier.getOwnerId().equals("testUser"));
+    Assert.assertTrue(btIdentifier.getBlockId().equals("101"));
+    Assert.assertTrue(btIdentifier.getAccessModes().equals(EnumSet
+        .allOf(AccessModeProto.class)));
+    Assert.assertTrue(btIdentifier.getOmCertSerialId().equals(omCertSerialId));
+
+    byte[] hash = secretManager.createPassword(btIdentifier);
+    validateHash(hash, btIdentifier.getBytes());
+  }
+
+  /**
+   * Validate hash using public key of KeyPair.
+   * */
+  private void validateHash(byte[] hash, byte[] identifier) throws Exception {
+    Signature rsaSignature =
+        Signature.getInstance(secretManager.getDefaultSignatureAlgorithm());
+    rsaSignature.initVerify(keyPair.getPublic());
+    rsaSignature.update(identifier);
+    Assert.assertTrue(rsaSignature.verify(hash));
+  }
+
+  @Test
+  public void testCreateIdentifierFailure() throws Exception {
+    LambdaTestUtils.intercept(SecurityException.class,
+        "Ozone block token can't be created without owner and access mode "
+            + "information.", () -> {
+          secretManager.createIdentifier();
+        });
+  }
+
+  @Test
+  public void testRenewToken() throws Exception {
+    LambdaTestUtils.intercept(UnsupportedOperationException.class,
+        "Renew token operation is not supported for ozone block" +
+            " tokens.", () -> {
+          secretManager.renewToken(null, null);
+        });
+  }
+
+  @Test
+  public void testCancelToken() throws Exception {
+    LambdaTestUtils.intercept(UnsupportedOperationException.class,
+        "Cancel token operation is not supported for ozone block" +
+            " tokens.", () -> {
+          secretManager.cancelToken(null, null);
+        });
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneDelegationTokenSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneDelegationTokenSecretManager.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneDelegationTokenSecretManager.java
new file mode 100644
index 0000000..37ad5ce
--- /dev/null
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneDelegationTokenSecretManager.java
@@ -0,0 +1,218 @@
+/*
+ * 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.ozone.security;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.hdds.HddsConfigKeys;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.test.LambdaTestUtils;
+import org.apache.hadoop.util.Time;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.Signature;
+
+/**
+ * Test class for {@link OzoneDelegationTokenSecretManager}.
+ */
+public class TestOzoneDelegationTokenSecretManager {
+
+  private OzoneDelegationTokenSecretManager<OzoneTokenIdentifier>
+      secretManager;
+  private SecurityConfig securityConfig;
+  private KeyPair keyPair;
+  private long expiryTime;
+  private Text serviceRpcAdd;
+  private OzoneConfiguration conf;
+  private static final String BASEDIR = GenericTestUtils.getTempPath(
+      TestOzoneDelegationTokenSecretManager.class.getSimpleName());
+  private final static Text TEST_USER = new Text("testUser");
+  private long tokenMaxLifetime = 1000 * 20;
+  private long tokenRemoverScanInterval = 1000 * 20;
+
+  @Before
+  public void setUp() throws Exception {
+    conf = new OzoneConfiguration();
+    conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, BASEDIR);
+    securityConfig = new SecurityConfig(conf);
+    // Create Ozone Master key pair.
+    keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
+    expiryTime = Time.monotonicNow() + 60 * 60 * 24;
+    serviceRpcAdd = new Text("localhost");
+  }
+
+  @After
+  public void tearDown() throws IOException {
+    secretManager.stop();
+    FileUtils.deleteQuietly(new File(BASEDIR));
+  }
+
+  @Test
+  public void testCreateToken() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    OzoneTokenIdentifier identifier =
+        OzoneTokenIdentifier.readProtoBuf(token.getIdentifier());
+    // Check basic details.
+    Assert.assertTrue(identifier.getRealUser().equals(TEST_USER));
+    Assert.assertTrue(identifier.getRenewer().equals(TEST_USER));
+    Assert.assertTrue(identifier.getOwner().equals(TEST_USER));
+
+    validateHash(token.getPassword(), token.getIdentifier());
+  }
+
+  @Test
+  public void testRenewTokenSuccess() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(10 * 5);
+    long renewalTime = secretManager.renewToken(token, TEST_USER.toString());
+    Assert.assertTrue(renewalTime > 0);
+  }
+
+  /**
+   * Tests failure for mismatch in renewer.
+   */
+  @Test
+  public void testRenewTokenFailure() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    LambdaTestUtils.intercept(AccessControlException.class,
+        "rougeUser tries to renew a token", () -> {
+          secretManager.renewToken(token, "rougeUser");
+        });
+  }
+
+  /**
+   * Tests token renew failure due to max time.
+   */
+  @Test
+  public void testRenewTokenFailureMaxTime() throws Exception {
+    secretManager = createSecretManager(conf, 100,
+        100, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(101);
+    LambdaTestUtils.intercept(IOException.class,
+        "testUser tried to renew an expired token", () -> {
+          secretManager.renewToken(token, TEST_USER.toString());
+        });
+  }
+
+  /**
+   * Tests token renew failure due to renewal time.
+   */
+  @Test
+  public void testRenewTokenFailureRenewalTime() throws Exception {
+    secretManager = createSecretManager(conf, 1000 * 10,
+        10, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(15);
+    LambdaTestUtils.intercept(IOException.class, "is expired", () -> {
+      secretManager.renewToken(token, TEST_USER.toString());
+    });
+  }
+
+  @Test
+  public void testCreateIdentifier() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    OzoneTokenIdentifier identifier = secretManager.createIdentifier();
+    // Check basic details.
+    Assert.assertTrue(identifier.getOwner().equals(new Text("")));
+    Assert.assertTrue(identifier.getRealUser().equals(new Text("")));
+    Assert.assertTrue(identifier.getRenewer().equals(new Text("")));
+  }
+
+  @Test
+  public void testCancelTokenSuccess() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    secretManager.cancelToken(token, TEST_USER.toString());
+  }
+
+  @Test
+  public void testCancelTokenFailure() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.start(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    LambdaTestUtils.intercept(AccessControlException.class,
+        "rougeUser is not authorized to cancel the token", () -> {
+          secretManager.cancelToken(token, "rougeUser");
+        });
+  }
+
+  /**
+   * Validate hash using public key of KeyPair.
+   */
+  private void validateHash(byte[] hash, byte[] identifier) throws Exception {
+    Signature rsaSignature =
+        Signature.getInstance(securityConfig.getSignatureAlgo(),
+            securityConfig.getProvider());
+    rsaSignature.initVerify(keyPair.getPublic());
+    rsaSignature.update(identifier);
+    Assert.assertTrue(rsaSignature.verify(hash));
+  }
+
+  /**
+   * Create instance of {@link OzoneDelegationTokenSecretManager}.
+   */
+  private OzoneDelegationTokenSecretManager<OzoneTokenIdentifier>
+      createSecretManager(OzoneConfiguration config, long tokenMaxLife,
+      long expiry, long tokenRemoverScanTime) throws IOException {
+    return new OzoneDelegationTokenSecretManager<>(config, tokenMaxLife,
+        expiry, tokenRemoverScanTime, serviceRpcAdd);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
deleted file mode 100644
index e4a8f2b..0000000
--- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
+++ /dev/null
@@ -1,216 +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.ozone.security;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.KeyPair;
-import java.security.Signature;
-import org.apache.commons.io.FileUtils;
-import org.apache.hadoop.hdds.HddsConfigKeys;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.hdds.security.x509.SecurityConfig;
-import org.apache.hadoop.io.Text;
-import org.apache.hadoop.security.AccessControlException;
-import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
-import org.apache.hadoop.security.token.Token;
-import org.apache.hadoop.test.GenericTestUtils;
-import org.apache.hadoop.test.LambdaTestUtils;
-import org.apache.hadoop.util.Time;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test class for {@link OzoneSecretManager}.
- */
-public class TestOzoneSecretManager {
-
-  private OzoneSecretManager<OzoneTokenIdentifier> secretManager;
-  private SecurityConfig securityConfig;
-  private KeyPair keyPair;
-  private long expiryTime;
-  private Text serviceRpcAdd;
-  private OzoneConfiguration conf;
-  private static final String BASEDIR = GenericTestUtils
-      .getTempPath(TestOzoneSecretManager.class.getSimpleName());
-  private final static Text TEST_USER = new Text("testUser");
-  private long tokenMaxLifetime = 1000 * 20;
-  private long tokenRemoverScanInterval = 1000 * 20;
-
-  @Before
-  public void setUp() throws Exception {
-    conf = new OzoneConfiguration();
-    conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, BASEDIR);
-    securityConfig = new SecurityConfig(conf);
-    // Create Ozone Master key pair.
-    keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
-    expiryTime = Time.monotonicNow() + 60 * 60 * 24;
-    serviceRpcAdd = new Text("localhost");
-  }
-
-  @After
-  public void tearDown() throws IOException {
-    secretManager.stop();
-    FileUtils.deleteQuietly(new File(BASEDIR));
-  }
-
-  @Test
-  public void testCreateToken() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    OzoneTokenIdentifier identifier =
-        OzoneTokenIdentifier.readProtoBuf(token.getIdentifier());
-    // Check basic details.
-    Assert.assertTrue(identifier.getRealUser().equals(TEST_USER));
-    Assert.assertTrue(identifier.getRenewer().equals(TEST_USER));
-    Assert.assertTrue(identifier.getOwner().equals(TEST_USER));
-
-    validateHash(token.getPassword(), token.getIdentifier());
-  }
-
-  @Test
-  public void testRenewTokenSuccess() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    Thread.sleep(10 * 5);
-    long renewalTime = secretManager.renewToken(token, TEST_USER.toString());
-    Assert.assertTrue(renewalTime > 0);
-  }
-
-  /**
-   * Tests failure for mismatch in renewer.
-   */
-  @Test
-  public void testRenewTokenFailure() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    LambdaTestUtils.intercept(AccessControlException.class,
-        "rougeUser tries to renew a token", () -> {
-          secretManager.renewToken(token, "rougeUser");
-        });
-  }
-
-  /**
-   * Tests token renew failure due to max time.
-   */
-  @Test
-  public void testRenewTokenFailureMaxTime() throws Exception {
-    secretManager = createSecretManager(conf, 100,
-        100, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    Thread.sleep(101);
-    LambdaTestUtils.intercept(IOException.class,
-        "testUser tried to renew an expired token", () -> {
-          secretManager.renewToken(token, TEST_USER.toString());
-        });
-  }
-
-  /**
-   * Tests token renew failure due to renewal time.
-   */
-  @Test
-  public void testRenewTokenFailureRenewalTime() throws Exception {
-    secretManager = createSecretManager(conf, 1000 * 10,
-        10, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    Thread.sleep(15);
-    LambdaTestUtils.intercept(IOException.class, "is expired", () -> {
-      secretManager.renewToken(token, TEST_USER.toString());
-    });
-  }
-
-  @Test
-  public void testCreateIdentifier() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    OzoneTokenIdentifier identifier = secretManager.createIdentifier();
-    // Check basic details.
-    Assert.assertTrue(identifier.getOwner().equals(new Text("")));
-    Assert.assertTrue(identifier.getRealUser().equals(new Text("")));
-    Assert.assertTrue(identifier.getRenewer().equals(new Text("")));
-  }
-
-  @Test
-  public void testCancelTokenSuccess() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    secretManager.cancelToken(token, TEST_USER.toString());
-  }
-
-  @Test
-  public void testCancelTokenFailure() throws Exception {
-    secretManager = createSecretManager(conf, tokenMaxLifetime,
-        expiryTime, tokenRemoverScanInterval);
-    secretManager.startThreads(keyPair);
-    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
-        TEST_USER,
-        TEST_USER);
-    LambdaTestUtils.intercept(AccessControlException.class,
-        "rougeUser is not authorized to cancel the token", () -> {
-          secretManager.cancelToken(token, "rougeUser");
-        });
-  }
-
-  /**
-   * Validate hash using public key of KeyPair.
-   */
-  private void validateHash(byte[] hash, byte[] identifier) throws Exception {
-    Signature rsaSignature =
-        Signature.getInstance(securityConfig.getSignatureAlgo(),
-            securityConfig.getProvider());
-    rsaSignature.initVerify(keyPair.getPublic());
-    rsaSignature.update(identifier);
-    Assert.assertTrue(rsaSignature.verify(hash));
-  }
-
-  /**
-   * Create instance of {@link OzoneSecretManager}.
-   */
-  private OzoneSecretManager<OzoneTokenIdentifier> createSecretManager(
-      OzoneConfiguration config, long tokenMaxLife, long expiry, long
-      tokenRemoverScanTime) throws IOException {
-    return new OzoneSecretManager<>(config, tokenMaxLife,
-        expiry, tokenRemoverScanTime, serviceRpcAdd);
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index c6616aa..1e49779 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -49,12 +49,10 @@ import org.apache.hadoop.ipc.RPC;
 import org.apache.hadoop.ozone.OzoneSecurityUtil;
 import org.apache.hadoop.ozone.security.OzoneSecurityException;
 import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
-import org.apache.hadoop.ozone.security.OzoneSecretManager;
 import org.apache.hadoop.security.AccessControlException;
 import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
 import org.apache.hadoop.metrics2.util.MBeans;
 import org.apache.hadoop.net.NetUtils;
-import org.apache.hadoop.ozone.OmUtils;
 import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.audit.AuditAction;
 import org.apache.hadoop.ozone.audit.AuditEventStatus;
@@ -79,6 +77,7 @@ import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolPB;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServicePort;
 import org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
+import org.apache.hadoop.ozone.security.OzoneDelegationTokenSecretManager;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
@@ -108,9 +107,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
-import java.util.concurrent.TimeUnit;
 
-import static org.apache.hadoop.ozone.security.OzoneSecurityException.ResultCodes.*;
 import static org.apache.hadoop.hdds.HddsUtils.getScmAddressForBlockClients;
 import static org.apache.hadoop.hdds.HddsUtils.getScmAddressForClients;
 import static org.apache.hadoop.hdds.HddsUtils.isHddsEnabled;
@@ -155,8 +152,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
           + StartupOption.HELP.getName() + " ]\n";
   private static final String OM_DAEMON = "om";
   private static boolean securityEnabled = false;
-  private static OzoneSecretManager secretManager;
-  // TO DO: For testing purpose only, remove before commiting
+  private static OzoneDelegationTokenSecretManager<OzoneTokenIdentifier>
+      secretManager;
   private KeyPair keyPair;
   private CertificateClient certClient;
   private static boolean testSecureOmFlag = false;
@@ -296,9 +293,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   }
 
 
-  private OzoneSecretManager createSecretManager(
-      OzoneConfiguration conf)
-      throws IOException {
+  private OzoneDelegationTokenSecretManager createSecretManager(
+      OzoneConfiguration conf) throws IOException {
     long tokenRemoverScanInterval =
         conf.getTimeDuration(OMConfigKeys.DELEGATION_REMOVER_SCAN_INTERVAL_KEY,
             OMConfigKeys.DELEGATION_REMOVER_SCAN_INTERVAL_DEFAULT,
@@ -311,8 +307,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
         conf.getTimeDuration(OMConfigKeys.DELEGATION_TOKEN_RENEW_INTERVAL_KEY,
             OMConfigKeys.DELEGATION_TOKEN_RENEW_INTERVAL_DEFAULT,
             TimeUnit.MILLISECONDS);
-    return new OzoneSecretManager(conf, tokenMaxLifetime, tokenRenewInterval,
-        tokenRemoverScanInterval, omRpcAddressTxt);
+    return new OzoneDelegationTokenSecretManager<>(conf, tokenMaxLifetime,
+        tokenRenewInterval, tokenRemoverScanInterval, omRpcAddressTxt);
   }
 
   private void stopSecretManager() throws IOException {
@@ -327,7 +323,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
       try {
         readKeyPair();
         LOG.info("Starting OM secret manager");
-        secretManager.startThreads(keyPair);
+        secretManager.start(keyPair);
       } catch (IOException e) {
         // Inability to start secret manager
         // can't be recovered from.
@@ -351,7 +347,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
           certClient.getPrivateKey(OM_DAEMON));
     } catch (Exception e) {
       throw new OzoneSecurityException("Error reading private file for "
-          + "OzoneManager", e, OM_PUBLIC_PRIVATE_KEY_FILE_NOT_EXIST);
+          + "OzoneManager", e, OzoneSecurityException
+          .ResultCodes.OM_PUBLIC_PRIVATE_KEY_FILE_NOT_EXIST);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/hadoop/blob/187bbbe6/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneManagerBlockToken.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneManagerBlockToken.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneManagerBlockToken.java
new file mode 100644
index 0000000..cb7caf3
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/TestOzoneManagerBlockToken.java
@@ -0,0 +1,251 @@
+/*
+ * 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.ozone.security;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.Time;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test class for OzoneManagerDelegationToken.
+ */
+public class TestOzoneManagerBlockToken {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(TestOzoneManagerBlockToken.class);
+  private static final String BASEDIR = GenericTestUtils
+      .getTempPath(TestOzoneManagerBlockToken.class.getSimpleName());
+  private static final String KEYSTORES_DIR =
+      new File(BASEDIR).getAbsolutePath();
+  private static long expiryTime;
+  private static KeyPair keyPair;
+  private static X509Certificate cert;
+  private static final long MAX_LEN = 1000;
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    File base = new File(BASEDIR);
+    FileUtil.fullyDelete(base);
+    base.mkdirs();
+    expiryTime = Time.monotonicNow() + 60 * 60 * 24;
+
+    // Create Ozone Master key pair.
+    keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
+    // Create Ozone Master certificate (SCM CA issued cert) and key store.
+    cert = KeyStoreTestUtil
+        .generateCertificate("CN=OzoneMaster", keyPair, 30, "SHA256withRSA");
+  }
+
+  @After
+  public void cleanUp() {
+  }
+
+  @Test
+  public void testSignToken() throws GeneralSecurityException, IOException {
+    String keystore = new File(KEYSTORES_DIR, "keystore.jks")
+        .getAbsolutePath();
+    String truststore = new File(KEYSTORES_DIR, "truststore.jks")
+        .getAbsolutePath();
+    String trustPassword = "trustPass";
+    String keyStorePassword = "keyStorePass";
+    String keyPassword = "keyPass";
+
+
+    KeyStoreTestUtil.createKeyStore(keystore, keyStorePassword, keyPassword,
+        "OzoneMaster", keyPair.getPrivate(), cert);
+
+    // Create trust store and put the certificate in the trust store
+    Map<String, X509Certificate> certs = Collections.singletonMap("server",
+        cert);
+    KeyStoreTestUtil.createTrustStore(truststore, trustPassword, certs);
+
+    // Sign the OzoneMaster Token with Ozone Master private key
+    PrivateKey privateKey = keyPair.getPrivate();
+    OzoneBlockTokenIdentifier tokenId = new OzoneBlockTokenIdentifier(
+        "testUser", "84940",
+        EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+        expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
+    byte[] signedToken = signTokenAsymmetric(tokenId, privateKey);
+
+    // Verify a valid signed OzoneMaster Token with Ozone Master
+    // public key(certificate)
+    boolean isValidToken = verifyTokenAsymmetric(tokenId, signedToken, cert);
+    LOG.info("{} is {}", tokenId, isValidToken ? "valid." : "invalid.");
+
+    // Verify an invalid signed OzoneMaster Token with Ozone Master
+    // public key(certificate)
+    tokenId = new OzoneBlockTokenIdentifier("", "",
+        EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+        expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
+    LOG.info("Unsigned token {} is {}", tokenId,
+        verifyTokenAsymmetric(tokenId, RandomUtils.nextBytes(128), cert));
+
+  }
+
+  public byte[] signTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
+      PrivateKey privateKey) throws NoSuchAlgorithmException,
+      InvalidKeyException, SignatureException {
+    Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+    rsaSignature.initSign(privateKey);
+    rsaSignature.update(tokenId.getBytes());
+    byte[] signature = rsaSignature.sign();
+    return signature;
+  }
+
+  public boolean verifyTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
+      byte[] signature, Certificate certificate) throws InvalidKeyException,
+      NoSuchAlgorithmException, SignatureException {
+    Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+    rsaSignature.initVerify(certificate);
+    rsaSignature.update(tokenId.getBytes());
+    boolean isValid = rsaSignature.verify(signature);
+    return isValid;
+  }
+
+  private byte[] signTokenSymmetric(OzoneBlockTokenIdentifier identifier,
+      Mac mac, SecretKey key) {
+    try {
+      mac.init(key);
+    } catch (InvalidKeyException ike) {
+      throw new IllegalArgumentException("Invalid key to HMAC computation",
+          ike);
+    }
+    return mac.doFinal(identifier.getBytes());
+  }
+
+  OzoneBlockTokenIdentifier generateTestToken() {
+    return new OzoneBlockTokenIdentifier(RandomStringUtils.randomAlphabetic(6),
+        RandomStringUtils.randomAlphabetic(5),
+        EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+        expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
+  }
+
+  @Test
+  public void testAsymmetricTokenPerf() throws NoSuchAlgorithmException,
+      CertificateEncodingException, NoSuchProviderException,
+      InvalidKeyException, SignatureException {
+    final int testTokenCount = 1000;
+    List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
+    List<byte[]> tokenPasswordAsym = new ArrayList<>();
+    for (int i = 0; i < testTokenCount; i++) {
+      tokenIds.add(generateTestToken());
+    }
+
+    KeyPair kp = KeyStoreTestUtil.generateKeyPair("RSA");
+
+    // Create Ozone Master certificate (SCM CA issued cert) and key store
+    X509Certificate omCert;
+    omCert = KeyStoreTestUtil.generateCertificate("CN=OzoneMaster",
+        kp, 30, "SHA256withRSA");
+
+    long startTime = Time.monotonicNowNanos();
+    for (int i = 0; i < testTokenCount; i++) {
+      tokenPasswordAsym.add(
+          signTokenAsymmetric(tokenIds.get(i), kp.getPrivate()));
+    }
+    long duration = Time.monotonicNowNanos() - startTime;
+    LOG.info("Average token sign time with HmacSha256(RSA/1024 key) is {} ns",
+        duration / testTokenCount);
+
+    startTime = Time.monotonicNowNanos();
+    for (int i = 0; i < testTokenCount; i++) {
+      verifyTokenAsymmetric(tokenIds.get(i), tokenPasswordAsym.get(i), omCert);
+    }
+    duration = Time.monotonicNowNanos() - startTime;
+    LOG.info("Average token verify time with HmacSha256(RSA/1024 key) "
+        + "is {} ns", duration / testTokenCount);
+  }
+
+  @Test
+  public void testSymmetricTokenPerf() {
+    String hmacSHA1 = "HmacSHA1";
+    String hmacSHA256 = "HmacSHA256";
+
+    testSymmetricTokenPerfHelper(hmacSHA1, 64);
+    testSymmetricTokenPerfHelper(hmacSHA256, 1024);
+  }
+
+  public void testSymmetricTokenPerfHelper(String hmacAlgorithm, int keyLen) {
+    final int testTokenCount = 1000;
+    List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
+    List<byte[]> tokenPasswordSym = new ArrayList<>();
+    for (int i = 0; i < testTokenCount; i++) {
+      tokenIds.add(generateTestToken());
+    }
+
+    KeyGenerator keyGen;
+    try {
+      keyGen = KeyGenerator.getInstance(hmacAlgorithm);
+      keyGen.init(keyLen);
+    } catch (NoSuchAlgorithmException nsa) {
+      throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
+          " algorithm.");
+    }
+
+    Mac mac;
+    try {
+      mac = Mac.getInstance(hmacAlgorithm);
+    } catch (NoSuchAlgorithmException nsa) {
+      throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
+          " algorithm.");
+    }
+
+    SecretKey secretKey = keyGen.generateKey();
+
+    long startTime = Time.monotonicNowNanos();
+    for (int i = 0; i < testTokenCount; i++) {
+      tokenPasswordSym.add(
+          signTokenSymmetric(tokenIds.get(i), mac, secretKey));
+    }
+    long duration = Time.monotonicNowNanos() - startTime;
+    LOG.info("Average token sign time with {}({} symmetric key) is {} ns",
+        hmacAlgorithm, keyLen, duration / testTokenCount);
+  }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org


Mime
View raw message