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

keywhiz.service.resources.admin.SecretsResource Maven / Gradle / Ivy

There is a newer version: 0.10.1
Show newest version
/*
 * Copyright (C) 2015 Square, Inc.
 *
 * 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 keywhiz.service.resources.admin;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import io.dropwizard.auth.Auth;
import io.dropwizard.jersey.params.LongParam;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import keywhiz.api.CreateSecretRequest;
import keywhiz.api.SecretDetailResponse;
import keywhiz.api.model.Client;
import keywhiz.api.model.Group;
import keywhiz.api.model.SanitizedSecret;
import keywhiz.api.model.Secret;
import keywhiz.api.model.VersionGenerator;
import keywhiz.auth.User;
import keywhiz.service.daos.AclDAO;
import keywhiz.service.daos.AclDAO.AclDAOFactory;
import keywhiz.service.daos.SecretController;
import keywhiz.service.daos.SecretSeriesDAO;
import keywhiz.service.daos.SecretSeriesDAO.SecretSeriesDAOFactory;
import keywhiz.service.exceptions.ConflictException;
import org.jooq.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.String.format;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;

/**
 * @parentEndpointName secrets-admin
 *
 * @resourcePath /admin/secrets
 * @resourceDescription Create, retrieve, and delete secrets
 */
@Path("/admin/secrets")
@Produces(APPLICATION_JSON)
public class SecretsResource {
  private static final Logger logger = LoggerFactory.getLogger(SecretsResource.class);

  private final SecretController secretController;
  private final AclDAO aclDAO;
  private final SecretSeriesDAO secretSeriesDAO;

  @Inject public SecretsResource(SecretController secretController, AclDAOFactory aclDAOFactory,
      SecretSeriesDAOFactory secretSeriesDAOFactory) {
    this.secretController = secretController;
    this.aclDAO = aclDAOFactory.readwrite();
    this.secretSeriesDAO = secretSeriesDAOFactory.readwrite();
  }

  @VisibleForTesting SecretsResource(SecretController secretController, AclDAO aclDAO,
      SecretSeriesDAO secretSeriesDAO) {
    this.secretController = secretController;
    this.aclDAO = aclDAO;
    this.secretSeriesDAO = secretSeriesDAO;
  }

  /**
   * Retrieve Secret by a specified name and version, or all Secrets if name is not given
   *
   * @excludeParams user
   * @optionalParams name
   * @param name the name of the Secret to retrieve, if provided
   * @optionalParams version
   * @param version the version of the Secret to retrieve, if provided
   * @param nameOnly if set, the result only contains the id and name for the secrets.
   *
   * @description Returns a single Secret or a set of all Secrets for this user.
   * Used by Keywhiz CLI and the web ui.
   * @responseMessage 200 Found and retrieved Secret(s)
   * @responseMessage 404 Secret with given name not found (if name provided)
   */
  @GET
  public Response findSecrets(@Auth User user, @DefaultValue("") @QueryParam("name") String name,
      @DefaultValue("") @QueryParam("version") String version,
      @DefaultValue("") @QueryParam("nameOnly") String nameOnly) {
    if (name.isEmpty()) {
      if (nameOnly.isEmpty()) {
        return Response.ok().entity(listSecrets(user)).build();
      } else {
        return Response.ok().entity(listSecretsNameOnly(user)).build();
      }
    }
    return Response.ok().entity(retrieveSecret(user, name, version)).build();
  }

  protected List listSecrets(@Auth User user) {
    logger.info("User '{}' listing secrets.", user);
    return secretController.getSanitizedSecrets();
  }

  protected List listSecretsNameOnly(@Auth User user) {
    logger.info("User '{}' listing secrets.", user);
    return secretController.getSecretsNameOnly();
  }

  protected SanitizedSecret retrieveSecret(@Auth User user, String name, String version) {
    logger.info("User '{}' retrieving secret name={} version={}.", user, name, version);
    return sanitizedSecretFromNameAndVersion(name, version);
  }

  /**
   * Retrieve all versions for a Secret name
   *
   * @excludeParams user
   * @param name the name of the Secret to find all versions for
   *
   * @description Returns a list of all versions for the given secret.
   * Used by Keywhiz CLI and the web ui.
   * @responseMessage 200 Found and retrieved versions(s)
   * @responseMessage 400 Name not given
   */
  @Path("/versions")
  @GET
  public List getVersionsForSecretName(@Auth User user,
      @DefaultValue("") @QueryParam("name") String name) {
    if (name.isEmpty()) {
      // Must supply a secret name to find versions for
      throw new BadRequestException("Must supply secret name to find versions.");
    }
    return retrieveSecretVersions(user, name);
  }

  /** Finds all versions for the specified secret name **/
  protected List retrieveSecretVersions(User user, String name) {
    logger.info("User '{}' finding versions for secret '{}'.", user, name);
    return secretController.getVersionsForName(name);
  }

  /**
   * Create Secret
   *
   * @excludeParams user
   * @param request the JSON client request used to formulate the Secret
   *
   * @description Creates a Secret with the name from a valid secret request.
   * Used by Keywhiz CLI and the web ui.
   * @responseMessage 200 Successfully created Secret
   * @responseMessage 400 Secret with given name already exists
   */
  @POST
  @Consumes(APPLICATION_JSON)
  public Response createSecret(@Auth User user, @Valid CreateSecretRequest request) {

    logger.info("User '{}' creating secret '{}' {} versioning.",
        user, request.name, request.withVersion ? "with" : "without");

    Secret secret;
    try {
      SecretController.SecretBuilder builder =
          secretController.builder(request.name, request.content, user.getName());

      if (request.description != null) {
        builder.withDescription(request.description);
      }

      if (request.metadata != null) {
        builder.withMetadata(request.metadata);
      }

      if (request.withVersion) {
        builder.withVersion(VersionGenerator.now().toHex());
      }

      secret = builder.build();
    } catch (DataAccessException e) {
      logger.warn("Cannot create secret {}: {}", request.name, e);
      throw new ConflictException(format("Cannot create secret %s.", request.name));
    }

    URI uri = UriBuilder.fromResource(SecretsResource.class).path("{secretId}").build(secret.getId());
    return Response
        .created(uri)
        .entity(secretDetailResponseFromId(secret.getId()))
        .build();
  }

  /**
   * Retrieve Secret by ID
   *
   * @excludeParams user
   * @param secretId the ID of the secret to retrieve
   *
   * @description Returns a single Secret if found.
   * Used by Keywhiz CLI and the web ui.
   * @responseMessage 200 Found and retrieved Secret with given ID
   * @responseMessage 404 Secret with given ID not Found
   */
  @Path("{secretId}")
  @GET
  public SecretDetailResponse retrieveSecret(@Auth User user,
      @PathParam("secretId") LongParam secretId) {

    logger.info("User '{}' retrieving secret id={}.", user, secretId);
    return secretDetailResponseFromId(secretId.get());
  }

  /**
   * Delete Secret by ID
   *
   * @excludeParams user
   * @param secretId the ID of the Secret to be deleted
   *
   * @description Deletes a single Secret if found.
   * Used by Keywhiz CLI and the web ui.
   * @responseMessage 200 Found and deleted Secret with given ID
   * @responseMessage 404 Secret with given ID not Found
   */
  @Path("{secretId}")
  @DELETE
  public Response deleteSecret(@Auth User user, @PathParam("secretId") LongParam secretId) {
    List secrets = secretController.getSecretsById(secretId.get());
    if (secrets.isEmpty()) {
      logger.info("User '{}' tried deleting not found id={}", user, secretId);
      throw new NotFoundException("Secret not found.");
    }

    for(Secret secret: secrets) {
      logger.info("User '{}' deleting secret id={}, name='{}'", user, secretId, secret.getDisplayName());
    }

    secretSeriesDAO.deleteSecretSeriesById(secretId.get());
    return Response.noContent().build();
  }

  private SecretDetailResponse secretDetailResponseFromId(long secretId) {
    List secrets = secretController.getSecretsById(secretId);
    if (secrets.isEmpty()) {
      throw new NotFoundException("Secret not found.");
    }

    // TODO(justin): API change needed to return all versions.
    Secret secret = secrets.get(0);
    ImmutableList groups = ImmutableList.copyOf(aclDAO.getGroupsFor(secret));
    ImmutableList clients = ImmutableList.copyOf(aclDAO.getClientsFor(secret));
    return SecretDetailResponse.fromSecret(secret, groups, clients);
  }

  private SanitizedSecret sanitizedSecretFromNameAndVersion(String name, String version) {
    Optional optionalSecret = secretController.getSecretByNameAndVersion(name, version);
    if (!optionalSecret.isPresent()) {
      throw new NotFoundException("Secret not found.");
    }

    Secret secret = optionalSecret.get();
    return SanitizedSecret.fromSecret(secret);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy