All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.cloud.hadoop.fs.gcs.auth.AbstractDelegationTokenBinding Maven / Gradle / Ivy

Go to download

An implementation of org.apache.hadoop.fs.FileSystem targeting Google Cloud Storage

There is a newer version: 3.0.4
Show newest version
/*
 * Copyright 2019 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.cloud.hadoop.fs.gcs.auth;

import static java.util.Objects.requireNonNull;

import com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystemBase;
import com.google.cloud.hadoop.util.AccessTokenProvider;
import com.google.common.flogger.GoogleLogger;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.token.SecretManager;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.delegation.web.DelegationTokenIdentifier;
import org.apache.hadoop.service.AbstractService;

/** Binds file system with service and access token provider */
public abstract class AbstractDelegationTokenBinding extends AbstractService {

  private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();

  private static final String SERVICE_NAME = "DelegationTokenBinding";

  private final Text kind;

  protected SecretManager secretManager = new TokenSecretManager();

  private Text service;

  /**
   * The owning filesystem. Valid after {@link #bindToFileSystem(GoogleHadoopFileSystemBase, Text)}.
   */
  private GoogleHadoopFileSystemBase fileSystem;

  protected AbstractDelegationTokenBinding(Text kind) {
    this(SERVICE_NAME, kind);
  }

  protected AbstractDelegationTokenBinding(String name, Text kind) {
    super(name);
    this.kind = kind;
  }

  public Text getKind() {
    return kind;
  }

  /** Returns the bound file system */
  public GoogleHadoopFileSystemBase getFileSystem() {
    return fileSystem;
  }

  public Text getService() {
    return service;
  }

  /**
   * Perform any actions when deploying unbonded, and return a list of credential providers.
   *
   * @throws IOException any failure.
   */
  public abstract AccessTokenProvider deployUnbonded() throws IOException;

  /**
   * Bind to the token identifier, returning the credential providers to use for the owner to talk
   * to GCP services.
   *
   * @param retrievedIdentifier the unmarshalled data
   * @return non-empty list of GCP credential providers to use for authenticating this client with
   *     GCP services.
   * @throws IOException any failure.
   */
  public abstract AccessTokenProvider bindToTokenIdentifier(
      DelegationTokenIdentifier retrievedIdentifier) throws IOException;

  /**
   * Bind to the filesystem. Subclasses can use this to perform their own binding operations - but
   * they must always call their superclass implementation. This Must be called before
   * calling {@code init()}.
   *
   * 

Important: This binding will happen during FileSystem.initialize(); the FS is not * live for actual use and will not yet have interacted with GCS services. * * @param fs owning FS. * @param service name of the service (i.e. bucket name) for the FS. */ public void bindToFileSystem(GoogleHadoopFileSystemBase fs, Text service) { this.fileSystem = requireNonNull(fs); this.service = requireNonNull(service); } /** * Create a delegation token for the user. This will only be called if a new DT is needed, that * is: the filesystem has been deployed unbound. * * @return the token * @throws IOException if one cannot be created */ public Token createDelegationToken(String renewer) throws IOException { Text renewerText = new Text(); if (renewer != null) { renewerText.set(renewer); } DelegationTokenIdentifier tokenIdentifier = requireNonNull(createTokenIdentifier(renewerText), "Token identifier"); Token token = new Token<>(tokenIdentifier, secretManager); token.setKind(getKind()); token.setService(service); logger.atFine().log("Created token %s with token identifier %s", token, tokenIdentifier); return token; } /** * Create a token identifier with all the information needed to be included in a delegation token. * This is where session credentials need to be extracted, etc. This will only be called if a new * DT is needed, that is: the filesystem has been deployed unbound. * *

If {@link #createDelegationToken} is overridden, this method can be replaced with a stub. * * @return the token data to include in the token identifier. * @throws IOException failure creating the token data. */ public abstract DelegationTokenIdentifier createTokenIdentifier(Text renewer) throws IOException; /** * Create a token identifier with all the information needed to be included in a delegation token. * This is where session credentials need to be extracted, etc. This will only be called if a new * DT is needed, that is: the filesystem has been deployed unbound. * *

If {@link #createDelegationToken} is overridden, this method can be replaced with a stub. * * @return the token data to include in the token identifier. * @throws IOException failure creating the token data. */ public abstract DelegationTokenIdentifier createTokenIdentifier() throws IOException; /** * Create a new "empty" token identifier. It is used by the "dummy" SecretManager, which requires * a token identifier (even one that's not real) to satisfy the contract. * * @return an empty identifier. */ public abstract DelegationTokenIdentifier createEmptyIdentifier(); /** * Verify that a token identifier is of a specific class. This will reject subclasses (i.e. it is * stricter than {@code instanceof}, then cast it to that type. * * @param identifier identifier to validate * @param expectedClass class of the expected token identifier. * @throws DelegationTokenIOException If the wrong class was found. */ @SuppressWarnings("unchecked") // safe by contract of convertTokenIdentifier() protected T convertTokenIdentifier( DelegationTokenIdentifier identifier, Class expectedClass) throws DelegationTokenIOException { if (identifier.getClass().equals(expectedClass)) { return (T) identifier; } throw DelegationTokenIOException.wrongTokenType(expectedClass, identifier); } /** * The secret manager always uses the same secret; the factory for new identifiers is that of the * token manager. */ protected class TokenSecretManager extends SecretManager { private final byte[] pwd = "not-a-password".getBytes(StandardCharsets.UTF_8); @Override protected byte[] createPassword(DelegationTokenIdentifier identifier) { return pwd; } @Override public byte[] retrievePassword(DelegationTokenIdentifier identifier) throws InvalidToken { return pwd; } @Override public DelegationTokenIdentifier createIdentifier() { return AbstractDelegationTokenBinding.this.createEmptyIdentifier(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy