private void registerExceptionMeteredAnnotations(final Method method, final ExceptionMetered classLevelExceptionMetered) { if (classLevelExceptionMetered != null) { exceptionMeters.putIfAbsent(method, new FeignOutboundMetricsDecorator.ExceptionMeterMetric(metricRegistry, method, classLevelExceptionMetered)); return; } final ExceptionMetered annotation = method.getAnnotation(ExceptionMetered.class); if (annotation != null) { exceptionMeters.putIfAbsent(method, new FeignOutboundMetricsDecorator.ExceptionMeterMetric(metricRegistry, method, annotation)); } }
@Test(expected = FeignException.class) public void exception() { stubFor(post(anyUrl()).willReturn(aResponse().withStatus(400))); MyClientWithAnnotationOnMethodLevel target = Feign.builder().invocationHandlerFactory( new FeignOutboundMetricsDecorator(new InvocationHandlerFactory.Default(), metricRegistry)) .target(MyClientWithAnnotationOnMethodLevel.class, String.format("http://localhost:%d", wireMockRule.port())); try { target.myMethod(); } finally { assertMetrics(); Set<Map.Entry<String, Meter>> entries = metricRegistry.getMeters().entrySet(); entries.forEach(entry -> { if (entry.getKey().endsWith(ExceptionMetered.DEFAULT_NAME_SUFFIX)) { assertEquals(String.format("wrong number of invocations in metric %s", entry.getKey()), 1, entry.getValue().getCount()); } }); } }
@Timed @ExceptionMetered public void dcParseRecord(String url) throws IOException, URISyntaxException, InterruptedException { Stopwatch stopwatch = Stopwatch.createStarted(); HttpGet httpGet = new HttpGet(url); try (CloseableHttpClient closeableHttpClient = HttpClients.createDefault()) { log.info("Send fetch list request {}", url); CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet); JAXBElement<OAIPMHtype> records = (JAXBElement<OAIPMHtype>) unmarshaller.unmarshal(new StreamSource(closeableHttpResponse.getEntity().getContent())); ListRecordsType list = records.getValue().getListRecords(); log.info("Submit {} record", list.getRecord().size()); recordListQueue.offer(list.getRecord()); if (list.getResumptionToken() != null && list.getResumptionToken().getValue().length() > 0) { URIBuilder uriBuilder = new URIBuilder(url); uriBuilder.setParameter("resumptionToken", list.getResumptionToken().getValue()); url = uriBuilder.build().toString(); log.info("Queue new url: {}, {}", list.getResumptionToken().getValue(), url); urlQueue.put(url); } } log.info("fetchListRecords() took {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); }
@Timed @ExceptionMetered public void fetchListRecords(String url) throws IOException, URISyntaxException, InterruptedException { Stopwatch stopwatch = Stopwatch.createStarted(); HttpGet httpGet = new HttpGet(url); try (CloseableHttpClient closeableHttpClient = HttpClients.createDefault()) { log.info("Send fetch list request {}", url); CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet); JAXBElement<OAIPMHtype> records = (JAXBElement<OAIPMHtype>) unmarshaller.unmarshal(new StreamSource(closeableHttpResponse.getEntity().getContent())); ListRecordsType list = records.getValue().getListRecords(); log.info("Submit {} record", list.getRecord().size()); recordListQueue.offer(list.getRecord()); if (list.getResumptionToken() != null && list.getResumptionToken().getValue().length() > 0) { URIBuilder uriBuilder = new URIBuilder(url); uriBuilder.setParameter("resumptionToken", list.getResumptionToken().getValue()); url = uriBuilder.build().toString(); log.info("Queue new url: {}, {}", list.getResumptionToken().getValue(), url); urlQueue.put(url); } } log.info("fetchListRecords() took {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); }
@GET @Timed(name = "showAll-timed-get") @Metered(name = "showAll-metered-get") @ExceptionMetered @CacheControl(maxAge = 12, maxAgeUnit = TimeUnit.HOURS) public List<ProductCatalogRepresentation> getAllProductCatalog() { LOGGER.info("Retrieving all product catalog details of the product"); List<ProductCatalog> details = productCatalogService.getAllProductDetails(); if (details == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } LOGGER.debug("Product details:" + details.toString()); List<ProductCatalogRepresentation> representations = new ArrayList<>(); for (ProductCatalog detail : details) { representations.add(ProductRepresentationDomainConverter.toRepresentation(detail)); } return representations; }
@GET @Path("/{skuId}") @Timed(name = "showAll-timed-get") @Metered(name = "showAll-metered-get") @ExceptionMetered @CacheControl(maxAge = 12, maxAgeUnit = TimeUnit.HOURS) public List<ProductReviewRepresentation> getAllProductReviews(@PathParam("skuId") String skuId) { LOGGER.info("Retrieving all product reviews of the product:" + skuId); List<ProductReview> reviews = productReviewService.getAllProductReviews(skuId); if (reviews == null || reviews.isEmpty()) { throw new WebApplicationException(Response.Status.NOT_FOUND); } List<ProductReviewRepresentation> representations = new ArrayList<>(); for (ProductReview review : reviews) { representations.add(ProductReviewRepresentationDomainConverter.toRepresentation(review)); } return representations; }
@GET @Path("/{id}") @Timed(name = "showAll-timed-get") @Metered(name = "showAll-metered-get") @ExceptionMetered @CacheControl(maxAge = 12, maxAgeUnit = TimeUnit.SECONDS) @JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT }) public ProductRepresentation getProductCatalog(@Auth User user, @PathParam("id") String id) throws Exception { LOGGER.info("Fetching the product catalog for the product with id:" + id); ProductRepresentation product = new ProductRepresentation(); JSONObject productCatalog = productCatalogClient.getProductCatalog(id); if (productCatalog == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } product.setProductCatalog((HashMap<String, Object>) productCatalog.toMap()); List<HashMap<String, Object>> reviewList = new ArrayList<>(); List<Object> reviews = productReviewClient.getProductReviews(id).toList(); for (Object review : reviews) { reviewList.add((HashMap<String, Object>) review); } product.setProductReviews(reviewList); return product; }
/** * This method will delete the {@link Customer} with the id provided. * * @param id * the unique identifier for this customer * @return {@link ResponseEntity} containing the HTTP headers and the HTTP * status code */ @RequestMapping(value = "/customer/{id}", method = RequestMethod.DELETE, produces = JSON_MIME_TYPE) @ResponseBody @Timed @ExceptionMetered public ResponseEntity<String> delete(@PathVariable Long id, HttpServletRequest request) { logApiCalls(request); try { Customer customer = customerService.findById(id); System.out.println(customer.toString() + " found and will be deleted"); } catch (NoSuchElementException e) { // Nothing to delete. } customerService.delete(id); return new ResponseEntity<String>(createHeaders(request.getMethod()), HttpStatus.NO_CONTENT); }
/** * Accept request and reply with OK. * * @param uriInfo Information about the URL for the request * @param asyncResponse The response object to send the final response to */ @GET @Timed(name = "logTimed") @Metered(name = "logMetered") @ExceptionMetered(name = "logException") @Produces(MediaType.APPLICATION_JSON) @Path("/log") public void getSucceed(@Context UriInfo uriInfo, @Suspended AsyncResponse asyncResponse) { RequestLog.startTiming(this); try { Thread.sleep(200); } catch (InterruptedException ignore) { // Do nothing } RequestLog.stopTiming(this); Response response = Response.status(Response.Status.OK).build(); asyncResponse.resume(response); }
/** * Accept request and reply with webapp exception and a simple string message. * * @param uriInfo Information about the URL for the request */ @GET @Timed(name = "logTimed") @Metered(name = "logMetered") @ExceptionMetered(name = "logException") @Produces(MediaType.APPLICATION_JSON) @Path("/webbug") public void getFailWithWebAppException(@Context UriInfo uriInfo) { RequestLog.startTiming(this); try { Thread.sleep(200); throw new WebApplicationException( Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Oops! Web App Exception").build() ); } catch (InterruptedException ignore) { // Do nothing } }
/** * Accept request and reply with a generic runtime exception and a simple string message. * * @param uriInfo Information about the URL for the request */ @GET @Timed(name = "logTimed") @Metered(name = "logMetered") @ExceptionMetered(name = "logException") @Produces(MediaType.APPLICATION_JSON) @Path("/genericbug") public void getFailWithRuntimeException(@Context UriInfo uriInfo) { RequestLog.startTiming(this); try { Thread.sleep(200); throw new RuntimeException("Oops! Generic Exception"); } catch (InterruptedException ignore) { // Do nothing } }
/** * Collect async responses in list then respond to all 1 second later. This keeps Grizzly happy. * * @param uriInfo Information about the URL for the request * @param asyncResponse The response object to send the final response to */ @GET @Timed(name = "testTimed") @Metered(name = "testMetered") @ExceptionMetered(name = "testExc") @Produces(MediaType.APPLICATION_JSON) @Path("/data") public void getData(@Context UriInfo uriInfo, @Suspended AsyncResponse asyncResponse) { synchronized (responses) { if (responses.size() == 0) { // start release thread new Thread(this).start(); } responses.add(new Pair<>(asyncResponse, RequestLog.dump())); } }
@DirectMethod @Timed @ExceptionMetered @Validate public UserXO authenticate(@NotEmpty final String base64Username, @NotEmpty final String base64Password) throws Exception { Subject subject = securitySystem.getSubject(); // FIXME: Subject is not nullable, but we have code that checks for nulls, likely from testing setups, verify and simplify checkState(subject != null); try { subject.login(new UsernamePasswordToken( Strings2.decodeBase64(base64Username), Strings2.decodeBase64(base64Password), false )); } catch (Exception e) { throw new Exception("Authentication failed", e); } return getUser(); }
/** * Logout and remove any session cookies * * @description Log out and remove any session cookies * @responseMessage 200 Logged out successfully */ @Timed @ExceptionMetered @POST @Produces(APPLICATION_JSON) public Response logout(@Nullable @CookieParam(value = "session") Cookie sessionCookie) { if (sessionCookie != null) { Optional<User> user = cookieAuthenticator.authenticate(sessionCookie); if (user.isPresent()) { logger.info("User logged out: {}", user.get().getName()); } else { logger.warn("Invalid user cookie on logout."); } } NewCookie expiredCookie = cookieFactory.getExpiredSessionCookie(); return Response.ok() .header(HttpHeaders.SET_COOKIE, expiredCookie.toString()) .build(); }
/** * Delete Client by ID * * @excludeParams user * @param clientId the ID of the Client to be deleted * * @description Deletes a single Client if found. * Used by Keywhiz CLI and the web ui. * @responseMessage 200 Found and deleted Client with given ID * @responseMessage 404 Client with given ID not Found */ @Path("{clientId}") @Timed @ExceptionMetered @DELETE public Response deleteClient(@Auth User user, @PathParam("clientId") LongParam clientId) { logger.info("User '{}' deleting client id={}.", user, clientId); Optional<Client> client = clientDAO.getClientById(clientId.get()); if (!client.isPresent()) { throw new NotFoundException("Client not found."); } clientDAO.deleteClient(client.get()); auditLog.recordEvent(new Event(Instant.now(), EventTag.CLIENT_DELETE, user.getName(), client.get().getName())); return Response.noContent().build(); }
@PUT @Timed @UnitOfWork @ExceptionMetered public User put(@Auth User existingUser, User user) { if (user.getPassword() != null) { final PasswordHashed passwordHashed = PasswordEncoder.encode(user.getPassword()); existingUser.setSalt(passwordHashed.salt()); existingUser.setPassword(passwordHashed.encodedPassword()); } if (user.getRole() != null) { existingUser.setRole(user.getRole()); } if (user.getUsername() != null) { throw new IllegalArgumentException("You cannot update username"); } _userDao.save(existingUser); return existingUser; }
/** * 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 nameOnly if set, the result only contains the id and name for the secrets. * @param idx if set, the desired starting index in a list of secrets to be retrieved * @param num if set, the number of secrets to retrieve * @param newestFirst whether to order the secrets by creation date with newest first; defaults to true * * @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) */ @Timed @ExceptionMetered @GET public Response findSecrets(@Auth User user, @DefaultValue("") @QueryParam("name") String name, @DefaultValue("") @QueryParam("nameOnly") String nameOnly, @QueryParam("idx") Integer idx, @QueryParam("num") Integer num, @DefaultValue("true") @QueryParam("newestFirst") Boolean newestFirst) { if (!name.isEmpty() && idx != null && num != null) { throw new BadRequestException("Name and idx/num cannot both be specified"); } validateArguments(name, nameOnly, idx, num); if (name.isEmpty()) { if (nameOnly.isEmpty()) { if (idx == null || num == null) { return Response.ok().entity(listSecrets(user)).build(); } else { return Response.ok().entity(listSecretsBatched(user, idx, num, newestFirst)).build(); } } else { return Response.ok().entity(listSecretsNameOnly(user)).build(); } } return Response.ok().entity(retrieveSecret(user, name)).build(); }
/** * Rollback to a previous secret version * * @param secretName the name of the secret to rollback * @param versionId the ID of the version to return to * @excludeParams user * @description Returns the previous versions of the secret if found Used by Keywhiz CLI. * @responseMessage 200 Found and reset the secret to this version * @responseMessage 404 Secret with given name not found or invalid version provided */ @Path("rollback/{secretName}/{versionId}") @Timed @ExceptionMetered @POST public Response resetSecretVersion(@Auth User user, @PathParam("secretName") String secretName, @PathParam("versionId") LongParam versionId) { logger.info("User '{}' rolling back secret '{}' to version with ID '{}'.", user, secretName, versionId); secretDAOReadWrite.setCurrentSecretVersionByName(secretName, versionId.get(), user.getName()); // If the secret wasn't found or the request was misformed, setCurrentSecretVersionByName // already threw an exception Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("new version", versionId.toString()); auditLog.recordEvent( new Event(Instant.now(), EventTag.SECRET_CHANGEVERSION, user.getName(), secretName, extraInfo)); // Send the new secret in response URI uri = UriBuilder.fromResource(SecretsResource.class).path("rollback/{secretName}/{versionID}").build(secretName, versionId); return Response.created(uri).entity(secretDetailResponseFromName(secretName)).build(); }
/** * 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}") @Timed @ExceptionMetered @DELETE public Response deleteSecret(@Auth User user, @PathParam("secretId") LongParam secretId) { Optional<Secret> secret = secretController.getSecretById(secretId.get()); if (!secret.isPresent()) { logger.info("User '{}' tried deleting a secret which was not found (id={})", user, secretId.get()); throw new NotFoundException("Secret not found."); } logger.info("User '{}' deleting secret id={}, name='{}'", user, secretId, secret.get().getName()); // Get the groups for this secret, so they can be restored manually if necessary Set<String> groups = aclDAOReadOnly.getGroupsFor(secret.get()).stream().map(Group::getName).collect(toSet()); secretDAOReadWrite.deleteSecretsByName(secret.get().getName()); // Record the deletion Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("groups", groups.toString()); extraInfo.put("current version", secret.get().getVersion().toString()); auditLog.recordEvent(new Event(Instant.now(), EventTag.SECRET_DELETE, user.getName(), secret.get().getName(), extraInfo)); return Response.noContent().build(); }
/** * Allow a Group to access this Secret * * @excludeParams user * @param secretId ID value of a Secret * @param groupId ID value of a Group * * @description Assigns the Secret specified by the secretID to the Group specified by the groupID * Used by Keywhiz CLI and the web ui. * @responseMessage 200 Successfully enrolled Secret in Group * @responseMessage 404 Could not find Secret or Group */ @Path("/secrets/{secretId}/groups/{groupId}") @Timed @ExceptionMetered @PUT public Response allowAccess( @Auth User user, @PathParam("secretId") LongParam secretId, @PathParam("groupId") LongParam groupId) { logger.info("User '{}' allowing groupId {} access to secretId {}", user, groupId, secretId); try { aclDAO.findAndAllowAccess(secretId.get(), groupId.get(), auditLog, user.getName(), new HashMap<>()); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Disallow a Group to access this Secret * * @excludeParams user * @param secretId ID value of a Secret * @param groupId ID value of a Group * * @description Unassigns the Secret specified by the secretID from the Group specified by the groupID * Used by Keywhiz CLI and the web ui. * @responseMessage 200 Successfully removed Secret from Group * @responseMessage 404 Could not find Secret or Group */ @Path("/secrets/{secretId}/groups/{groupId}") @Timed @ExceptionMetered @DELETE public Response disallowAccess( @Auth User user, @PathParam("secretId") LongParam secretId, @PathParam("groupId") LongParam groupId) { logger.info("User '{}' disallowing groupId {} access to secretId {}", user, groupId, secretId); try { aclDAO.findAndRevokeAccess(secretId.get(), groupId.get(), auditLog, user.getName(), new HashMap<>()); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Enroll a Client into a Group * * @excludeParams user * @param clientId ID value of a Client * @param groupId ID value of a Group * * @description Assigns the Client specified by the clientID to the Group specified by the groupID * @responseMessage 200 Successfully enrolled Client in Group * @responseMessage 404 Could not find Client or Group */ @Path("/clients/{clientId}/groups/{groupId}") @Timed @ExceptionMetered @PUT public Response enrollClient( @Auth User user, @PathParam("clientId") LongParam clientId, @PathParam("groupId") LongParam groupId) { logger.info("User {} enrolling clientId {} in groupId {}.", user.getName(), clientId, groupId); try { aclDAO.findAndEnrollClient(clientId.get(), groupId.get(), auditLog, user.getName(), new HashMap<>()); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Remove a Client from a Group * * @excludeParams user * @param clientId ID value of a Client * @param groupId ID value of a Group * * @description Unassigns the Client specified by the clientID from the Group specified by the groupID * @responseMessage 200 Successfully removed Client from Group * @responseMessage 404 Could not find Client or Group */ @Path("/clients/{clientId}/groups/{groupId}") @Timed @ExceptionMetered @DELETE public Response evictClient( @Auth User user, @PathParam("clientId") LongParam clientId, @PathParam("groupId") LongParam groupId) { logger.info("User {} evicting clientId {} from groupId {}.", user.getName(), clientId, groupId); try { aclDAO.findAndEvictClient(clientId.get(), groupId.get(), auditLog, user.getName(), new HashMap<>()); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Retrieve Client by ID * * @param clientId the ID of the Client to retrieve * @excludeParams automationClient * @description Returns a single Client if found * @responseMessage 200 Found and retrieved Client with given ID * @responseMessage 404 Client with given ID not Found */ @Timed @ExceptionMetered @GET @Path("{clientId}") public Response findClientById( @Auth AutomationClient automationClient, @PathParam("clientId") LongParam clientId) { logger.info("Automation ({}) - Looking up an ID {}", automationClient.getName(), clientId); Client client = clientDAO.getClientById(clientId.get()) .orElseThrow(NotFoundException::new); ImmutableList<Group> groups = ImmutableList.copyOf(aclDAO.getGroupsFor(client)); return Response.ok() .entity(ClientDetailResponse.fromClient(client, groups, ImmutableList.of())) .build(); }
/** * Retrieve Client by a specified name, or all Clients if no name given * * @param name the name of the Client to retrieve, if provided * @excludeParams automationClient * @optionalParams name * @description Returns a single Client or a set of all Clients * @responseMessage 200 Found and retrieved Client(s) * @responseMessage 404 Client with given name not found (if name provided) */ @Timed @ExceptionMetered @GET public Response findClient( @Auth AutomationClient automationClient, @QueryParam("name") Optional<String> name) { logger.info("Automation ({}) - Looking up a name {}", automationClient.getName(), name); if (name.isPresent()) { Client client = clientDAO.getClient(name.get()).orElseThrow(NotFoundException::new); ImmutableList<Group> groups = ImmutableList.copyOf(aclDAO.getGroupsFor(client)); return Response.ok() .entity(ClientDetailResponse.fromClient(client, groups, ImmutableList.of())) .build(); } List<ClientDetailResponse> clients = clientDAO.getClients().stream() .map(c -> ClientDetailResponse.fromClient(c, ImmutableList.copyOf(aclDAO.getGroupsFor(c)), ImmutableList.of())) .collect(toList()); return Response.ok().entity(clients).build(); }
/** * Enroll Client in Group * * @param clientId the ID of the Client to assign * @param groupId the ID of the Group to be assigned to * @excludeParams automationClient * @description Assigns the Client specified by the clientID to the Group specified by the * groupID * @responseMessage 200 Successfully enrolled Client in Group * @responseMessage 404 Could not find Client or Group */ @Timed @ExceptionMetered @PUT public Response enrollClientInGroup( @Auth AutomationClient automationClient, @PathParam("clientId") LongParam clientId, @PathParam("groupId") LongParam groupId) { try { Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("deprecated", "true"); aclDAO.findAndEnrollClient(clientId.get(), groupId.get(), auditLog, automationClient.getName(), extraInfo); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Remove Client from Group * * @param clientId the ID of the Client to unassign * @param groupId the ID of the Group to be removed from * @excludeParams automationClient * @description Unassigns the Client specified by the clientID from the Group specified by the * groupID * @responseMessage 200 Successfully removed Client from Group * @responseMessage 404 Could not find Client or Group */ @Timed @ExceptionMetered @DELETE public Response evictClientFromGroup( @Auth AutomationClient automationClient, @PathParam("clientId") long clientId, @PathParam("groupId") long groupId) { try { Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("deprecated", "true"); aclDAO.findAndEvictClient(clientId, groupId, auditLog, automationClient.getName(), extraInfo); } catch (IllegalStateException e) { throw new NotFoundException(); } return Response.ok().build(); }
/** * Retrieve secret by ID * * @excludeParams automationClient * @param secretId the ID of the secret to retrieve * * @description Returns a single secret if found * @responseMessage 200 Found and retrieved secret with given ID * @responseMessage 404 Secret with given ID not found */ @Path("{secretId}") @Timed @ExceptionMetered @GET public AutomationSecretResponse readSecretById( @Auth AutomationClient automationClient, @PathParam("secretId") LongParam secretId) { Optional<Secret> secret = secretController.getSecretById(secretId.get()); if (!secret.isPresent()) { throw new NotFoundException("Secret not found."); } ImmutableList<Group> groups = ImmutableList.copyOf(aclDAO.getGroupsFor(secret.get())); return AutomationSecretResponse.fromSecret(secret.get(), groups); }
/** * Creates a client and assigns to given groups * * @excludeParams automationClient * @param request JSON request to create a client * * @responseMessage 201 Created client and assigned to given groups * @responseMessage 409 Client already exists */ @Timed @ExceptionMetered @POST @Consumes(APPLICATION_JSON) public Response createClient(@Auth AutomationClient automationClient, @Valid CreateClientRequestV2 request) { String creator = automationClient.getName(); String client = request.name(); clientDAOReadWrite.getClient(client).ifPresent((c) -> { logger.info("Automation ({}) - Client {} already exists", creator, client); throw new ConflictException("Client name already exists."); }); // Creates new client record long clientId = clientDAOReadWrite.createClient(client, creator, request.description()); auditLog.recordEvent(new Event(Instant.now(), EventTag.CLIENT_CREATE, creator, client)); // Enrolls client in any requested groups groupsToGroupIds(request.groups()) .forEach((maybeGroupId) -> maybeGroupId.ifPresent( (groupId) -> aclDAOReadWrite.findAndEnrollClient(clientId, groupId, auditLog, creator, new HashMap<>()))); URI uri = UriBuilder.fromResource(ClientResource.class).path(client).build(); return Response.created(uri).build(); }
/** * Modify a client * * @excludeParams automationClient * @param currentName Client name * @param request JSON request to modify the client * * @responseMessage 201 Client updated * @responseMessage 404 Client not found */ @Timed @ExceptionMetered @POST @Path("{name}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public ClientDetailResponseV2 modifyClient(@Auth AutomationClient automationClient, @PathParam("name") String currentName, @Valid ModifyClientRequestV2 request) { Client client = clientDAOReadWrite.getClient(currentName) .orElseThrow(NotFoundException::new); String newName = request.name(); // TODO: implement change client (name, updatedAt, updatedBy) throw new NotImplementedException(format( "Need to implement mutation methods in DAO to rename %s to %s", client.getName(), newName)); }
/** * Creates a group * * @excludeParams automationClient * @param request JSON request to create a group * * @responseMessage 201 Created group * @responseMessage 409 Group already exists */ @Timed @ExceptionMetered @POST @Consumes(APPLICATION_JSON) public Response createGroup(@Auth AutomationClient automationClient, @Valid CreateGroupRequestV2 request) { String creator = automationClient.getName(); String group = request.name(); groupDAOReadWrite.getGroup(group).ifPresent((g) -> { logger.info("Automation ({}) - Group {} already exists", creator, group); throw new ConflictException(format("Group %s already exists", group)); }); groupDAOReadWrite.createGroup(group, creator, request.description(), request.metadata()); Map<String, String> extraInfo = new HashMap<>(); if (request.description() != null) { extraInfo.put("description", request.description()); } if (request.metadata() != null) { extraInfo.put("metadata", request.metadata().toString()); } auditLog.recordEvent(new Event(Instant.now(), EventTag.GROUP_CREATE, creator, group, extraInfo)); URI uri = UriBuilder.fromResource(GroupResource.class).path(group).build(); return Response.created(uri).build(); }
/** * Retrieve information on a group * * @excludeParams automationClient * @param name Group name * * @responseMessage 200 Group information retrieved * @responseMessage 404 Group not found */ @Timed @ExceptionMetered @GET @Path("{name}") @Produces(APPLICATION_JSON) public GroupDetailResponseV2 groupInfo(@Auth AutomationClient automationClient, @PathParam("name") String name) { Group group = groupDAOReadOnly.getGroup(name) .orElseThrow(NotFoundException::new); Set<String> secrets = aclDAOReadOnly.getSanitizedSecretsFor(group).stream() .map(SanitizedSecret::name) .collect(toSet()); Set<String> clients = aclDAOReadOnly.getClientsFor(group).stream() .map(Client::getName) .collect(toSet()); return GroupDetailResponseV2.builder() .group(group) .secrets(secrets) .clients(clients) .build(); }
/** * Updates a subset of the fields of an existing secret * * @excludeParams automationClient * @param request JSON request to update a secret * * @responseMessage 201 Created secret and assigned to given groups */ @Timed @ExceptionMetered @Path("{name}/partialupdate") @POST @Consumes(APPLICATION_JSON) public Response partialUpdateSecret(@Auth AutomationClient automationClient, @PathParam("name") String name, @Valid PartialUpdateSecretRequestV2 request) { secretDAO.partialUpdateSecret(name, automationClient.getName(), request); Map<String, String> extraInfo = new HashMap<>(); if (request.description() != null) { extraInfo.put("description", request.description()); } if (request.metadata() != null) { extraInfo.put("metadata", request.metadata().toString()); } if (request.expiry() != null) { extraInfo.put("expiry", Long.toString(request.expiry())); } auditLog.recordEvent(new Event(Instant.now(), EventTag.SECRET_UPDATE, automationClient.getName(), name, extraInfo)); UriBuilder uriBuilder = UriBuilder.fromResource(SecretResource.class).path(name); return Response.created(uriBuilder.build()).build(); }
/** * Retrieve listing of secret names. If "idx" and "num" are both provided, retrieve "num" * names starting at "idx" from a list of secret names ordered by creation date, with * order depending on "newestFirst" (which defaults to "true") * * @excludeParams automationClient * @param idx the index from which to start retrieval in the list of secret names * @param num the number of names to retrieve * @param newestFirst whether to list the most-recently-created names first * @responseMessage 200 List of secret names * @responseMessage 400 Invalid (negative) idx or num */ @Timed @ExceptionMetered @GET @Produces(APPLICATION_JSON) public Iterable<String> secretListing(@Auth AutomationClient automationClient, @QueryParam("idx") Integer idx, @QueryParam("num") Integer num, @DefaultValue("true") @QueryParam("newestFirst") boolean newestFirst) { if (idx != null && num != null) { if (idx < 0 || num < 0) { throw new BadRequestException( "Index and num must both be positive when retrieving batched secrets!"); } return secretControllerReadOnly.getSecretsBatched(idx, num, newestFirst).stream() .map(SanitizedSecret::name) .collect(toList()); } return secretControllerReadOnly.getSanitizedSecrets(null, null).stream() .map(SanitizedSecret::name) .collect(toSet()); }
/** * Retrieve listing of secrets. If "idx" and "num" are both provided, retrieve "num" * names starting at "idx" from a list of secrets ordered by creation date, with * order depending on "newestFirst" (which defaults to "true") * * @excludeParams automationClient * @param idx the index from which to start retrieval in the list of secrets * @param num the number of names to retrieve * @param newestFirst whether to list the most-recently-created names first * @responseMessage 200 List of secret names * @responseMessage 400 Invalid (negative) idx or num */ @Timed @ExceptionMetered @Path("/v2") @GET @Produces(APPLICATION_JSON) public Iterable<SanitizedSecret> secretListingV2(@Auth AutomationClient automationClient, @QueryParam("idx") Integer idx, @QueryParam("num") Integer num, @DefaultValue("true") @QueryParam("newestFirst") boolean newestFirst) { if (idx != null && num != null) { if (idx < 0 || num < 0) { throw new BadRequestException( "Index and num must both be positive when retrieving batched secrets!"); } return secretControllerReadOnly.getSecretsBatched(idx, num, newestFirst); } return secretControllerReadOnly.getSanitizedSecrets(null, null); }
/** * Retrieve the given range of versions of this secret, sorted from newest to * oldest update time. If versionIdx is nonzero, then numVersions versions, * starting from versionIdx in the list and increasing in index, will be * returned (set numVersions to a very large number to retrieve all versions). * For instance, versionIdx = 5 and numVersions = 10 will retrieve entries * at indices 5 through 14. * * @param name Secret series name * @param versionIdx The index in the list of versions of the first version to retrieve * @param numVersions The number of versions to retrieve * @excludeParams automationClient * @responseMessage 200 Secret series information retrieved * @responseMessage 404 Secret series not found */ @Timed @ExceptionMetered @GET @Path("{name}/versions") @Produces(APPLICATION_JSON) public Iterable<SecretDetailResponseV2> secretVersions(@Auth AutomationClient automationClient, @PathParam("name") String name, @QueryParam("versionIdx") int versionIdx, @QueryParam("numVersions") int numVersions) { ImmutableList<SanitizedSecret> versions = secretDAO.getSecretVersionsByName(name, versionIdx, numVersions) .orElseThrow(NotFoundException::new); return versions.stream() .map(v -> SecretDetailResponseV2.builder() .sanitizedSecret(v) .build()) .collect(toList()); }
public EventDriverMetrics(final Class<?> endpointClass, MetricRegistry metrics) { final Class<?> klass = endpointClass; Metered metered = klass.getAnnotation(Metered.class); Timed timed = klass.getAnnotation(Timed.class); ExceptionMetered em = klass.getAnnotation(ExceptionMetered.class); this.onTextMeter = metered != null ? Optional.of(metrics.meter(MetricRegistry.name(metered.name(), klass.getName(), OnMessage.class.getSimpleName()))) : Optional.empty(); this.countOpened = metered != null ? Optional.of(metrics.counter(MetricRegistry.name(metered.name(), klass.getName(), OPEN_CONNECTIONS))) : Optional.empty(); this.timer = timed != null ? Optional.of(metrics.timer(MetricRegistry.name(timed.name(), klass.getName()))) : Optional.empty(); this.exceptionMetered = em != null ? Optional.of(metrics.meter(MetricRegistry.name(em.name(), klass.getName(), OnError.class.getSimpleName()))) : Optional.empty(); }
/** * Delete a secret series * * @excludeParams automationClient * @param name Secret series name * * @responseMessage 204 Secret series deleted * @responseMessage 404 Secret series not found */ @Timed @ExceptionMetered @DELETE @Path("{name}") public Response deleteSecretSeries(@Auth AutomationClient automationClient, @PathParam("name") String name) { Secret secret = secretController.getSecretByName(name).orElseThrow(() -> new NotFoundException("Secret series not found.")); // Get the groups for this secret so they can be restored manually if necessary Set<String> groups = aclDAO.getGroupsFor(secret).stream().map(Group::getName).collect(toSet()); secretDAO.deleteSecretsByName(name); // Record the deletion in the audit log Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("groups", groups.toString()); extraInfo.put("current version", secret.getVersion().toString()); auditLog.recordEvent(new Event(Instant.now(), EventTag.SECRET_DELETE, automationClient.getName(), name, extraInfo)); return Response.noContent().build(); }
/** * Retrieve Group by a specified name, or all Groups if no name given * * @param name the name of the Group to retrieve, if provided * @excludeParams automationClient * @optionalParams name * @description Returns a single Group or a set of all Groups * @responseMessage 200 Found and retrieved Group(s) * @responseMessage 404 Group with given name not found (if name provided) */ @Timed @ExceptionMetered @GET public Response getGroupByName( @Auth AutomationClient automationClient, @QueryParam("name") Optional<String> name) { if (name.isPresent()) { Group group = groupDAO.getGroup(name.get()).orElseThrow(NotFoundException::new); ImmutableList<Client> clients = ImmutableList.copyOf(aclDAO.getClientsFor(group)); ImmutableList<SanitizedSecret> sanitizedSecrets = ImmutableList.copyOf(aclDAO.getSanitizedSecretsFor(group)); return Response.ok() .entity(GroupDetailResponse.fromGroup(group, sanitizedSecrets, clients)) .build(); } ImmutableList<SanitizedSecret> emptySecrets = ImmutableList.of(); ImmutableList<Client> emptyClients = ImmutableList.of(); List<GroupDetailResponse> groups = groupDAO.getGroups().stream() .map((g) -> GroupDetailResponse.fromGroup(g, emptySecrets, emptyClients)) .collect(toList()); return Response.ok() .entity(groups) .build(); }
/** * Deletes a group * * @param groupId the ID of the group to delete * @excludeParams automationClient * @description Deletes a single group by id * @responseMessage 200 Deleted group * @responseMessage 404 Group not found by id */ @Timed @ExceptionMetered @DELETE @Path("{groupId}") public Response deleteGroup( @Auth AutomationClient automationClient, @PathParam("groupId") LongParam groupId) { Group group = groupDAO.getGroupById(groupId.get()).orElseThrow(NotFoundException::new); groupDAO.deleteGroup(group); Map<String, String> extraInfo = new HashMap<>(); extraInfo.put("deprecated", "true"); auditLog.recordEvent( new Event(Instant.now(), EventTag.GROUP_DELETE, automationClient.getName(), group.getName(), extraInfo)); return Response.ok().build(); }