com.netflix.spinnaker.security.AuthenticatedRequest Java Examples

The following examples show how to use com.netflix.spinnaker.security.AuthenticatedRequest. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: AtlasStorageUpdater.java    From kayenta with Apache License 2.0 6 votes vote down vote up
boolean run(
    RetrofitClientFactory retrofitClientFactory,
    ObjectMapper objectMapper,
    OkHttpClient okHttpClient) {
  RemoteService remoteService = new RemoteService();
  remoteService.setBaseUrl(uri);
  AtlasStorageRemoteService atlasStorageRemoteService =
      retrofitClientFactory.createClient(
          AtlasStorageRemoteService.class,
          new JacksonConverter(objectMapper),
          remoteService,
          okHttpClient);
  try {
    Map<String, Map<String, AtlasStorage>> atlasStorageMap =
        AuthenticatedRequest.allowAnonymous(atlasStorageRemoteService::fetch);
    atlasStorageDatabase.update(atlasStorageMap);
  } catch (RetrofitError e) {
    log.warn("While fetching atlas backends from " + uri, e);
    return succeededAtLeastOnce;
  }
  succeededAtLeastOnce = true;
  return true;
}
 
Example #2
Source File: BackendUpdater.java    From kayenta with Apache License 2.0 6 votes vote down vote up
boolean run(
    RetrofitClientFactory retrofitClientFactory,
    ObjectMapper objectMapper,
    OkHttpClient okHttpClient) {
  RemoteService remoteService = new RemoteService();
  remoteService.setBaseUrl(uri);
  BackendsRemoteService backendsRemoteService =
      retrofitClientFactory.createClient(
          BackendsRemoteService.class,
          new JacksonConverter(objectMapper),
          remoteService,
          okHttpClient);
  try {
    List<Backend> backends = AuthenticatedRequest.allowAnonymous(backendsRemoteService::fetch);
    backendDatabase.update(backends);
  } catch (RetrofitError e) {
    log.warn("While fetching atlas backends from " + uri, e);
    return succeededAtLeastOnce;
  }
  succeededAtLeastOnce = true;
  return true;
}
 
Example #3
Source File: PluginInfoService.java    From front50 with Apache License 2.0 6 votes vote down vote up
/** Set the preferred release. If preferred is true, sets previous preferred release to false. */
public PluginInfo.Release preferReleaseVersion(
    @Nonnull String id, @Nonnull String releaseVersion, boolean preferred) {
  PluginInfo pluginInfo = repository.findById(id);
  Optional<PluginInfo.Release> release = pluginInfo.getReleaseByVersion(releaseVersion);

  Instant now = Instant.now();
  String user = AuthenticatedRequest.getSpinnakerUser().orElse("anonymous");

  return release
      .map(
          r -> {
            r.setPreferred(preferred);
            r.setLastModified(now);
            r.setLastModifiedBy(user);

            pluginInfo.setReleaseByVersion(releaseVersion, r);
            cleanupPreferredReleases(pluginInfo, r);

            repository.update(pluginInfo.getId(), pluginInfo);
            return r;
          })
      .orElse(null);
}
 
Example #4
Source File: PluginInfoService.java    From front50 with Apache License 2.0 6 votes vote down vote up
public PluginInfo upsertRelease(@Nonnull String id, @Nonnull PluginInfo.Release release) {
  release.setLastModifiedBy(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"));
  release.setLastModified(Instant.now());
  PluginInfo pluginInfo = repository.findById(id);
  Optional<PluginInfo.Release> existingRelease =
      pluginInfo.getReleaseByVersion(release.getVersion());

  return existingRelease
      .map(
          r -> {
            pluginInfo.getReleases().remove(r);
            pluginInfo.getReleases().add(release);
            cleanupPreferredReleases(pluginInfo, release);
            validate(pluginInfo);
            repository.update(pluginInfo.getId(), pluginInfo);
            return pluginInfo;
          })
      .orElseThrow(
          () ->
              new NotFoundException(
                  String.format(
                      "Plugin %s with release %s version not found. ",
                      id, release.getVersion())));
}
 
Example #5
Source File: GoogleCloudBuildArtifactExtractor.java    From echo with Apache License 2.0 6 votes vote down vote up
@Override
public List<Artifact> getArtifacts(String messagePayload) {
  TypedInput build =
      new TypedByteArray("application/json", messagePayload.getBytes(StandardCharsets.UTF_8));
  try {
    return retrySupport.retry(
        () ->
            AuthenticatedRequest.allowAnonymous(
                () -> igorService.extractGoogleCloudBuildArtifacts(account, build)),
        5,
        2000,
        false);
  } catch (Exception e) {
    log.error("Failed to fetch artifacts for build: {}", e);
    return Collections.emptyList();
  }
}
 
Example #6
Source File: EventPropagator.java    From echo with Apache License 2.0 6 votes vote down vote up
public void processEvent(Event event) {
  Observable.from(listeners)
      .map(
          listener ->
              AuthenticatedRequest.propagate(
                  () -> {
                    listener.processEvent(event);
                    return null;
                  }))
      .observeOn(scheduler)
      .subscribe(
          callable -> {
            try {
              callable.call();
            } catch (Exception e) {
              log.error("failed processing event: {}", event, e);
            }
          });
}
 
Example #7
Source File: GoogleCloudBuildNotificationAgent.java    From echo with Apache License 2.0 6 votes vote down vote up
@Override
public void processEvent(Event event) {
  if (event.getDetails() != null && event.getDetails().getType().equals("googleCloudBuild")) {
    MessageDescription messageDescription =
        (MessageDescription) event.getContent().get("messageDescription");
    retrySupport.retry(
        () ->
            AuthenticatedRequest.allowAnonymous(
                () ->
                    igorService.updateBuildStatus(
                        messageDescription.getSubscriptionName(),
                        messageDescription.getMessageAttributes().get("buildId"),
                        messageDescription.getMessageAttributes().get("status"),
                        new TypedByteArray(
                            "application/json",
                            messageDescription
                                .getMessagePayload()
                                .getBytes(StandardCharsets.UTF_8)))),
        5,
        2000,
        false);
  }
}
 
Example #8
Source File: PipelineInitiator.java    From echo with Apache License 2.0 6 votes vote down vote up
/**
 * The set of accounts that a user has WRITE access to.
 *
 * <p>Similar filtering can be found in `gate` (see AllowedAccountsSupport.java).
 *
 * @param user A service account name (or 'anonymous' if not specified)
 * @return the allowed accounts for {@param user} as determined by fiat
 */
private Set<String> getAllowedAccountsForUser(String user) {
  if (fiatPermissionEvaluator == null || !fiatStatus.isLegacyFallbackEnabled()) {
    return Collections.emptySet();
  }

  UserPermission.View userPermission = null;
  try {
    userPermission =
        AuthenticatedRequest.allowAnonymous(() -> fiatPermissionEvaluator.getPermission(user));
  } catch (Exception e) {
    log.error("Unable to fetch permission for {}", user, e);
  }

  if (userPermission == null) {
    return Collections.emptySet();
  }

  return userPermission.getAccounts().stream()
      .filter(v -> v.getAuthorizations().contains(Authorization.WRITE))
      .map(Account.View::getName)
      .collect(Collectors.toSet());
}
 
Example #9
Source File: BaseTriggerEventHandler.java    From echo with Apache License 2.0 5 votes vote down vote up
protected boolean canAccessApplication(Trigger trigger) {
  String runAsUser = trigger.getRunAsUser();
  if (runAsUser == null) {
    runAsUser = "anonymous";
  }
  String user = runAsUser;
  String application = trigger.getParent().getApplication();
  boolean hasPermission =
      AuthenticatedRequest.allowAnonymous(
          () ->
              fiatPermissionEvaluator.hasPermission(user, application, "APPLICATION", "EXECUTE"));
  if (!hasPermission) {
    log.info(
        "The user '{}' does not have access to execute pipelines in the application '{}', skipped triggering of pipeline '{}'.",
        user,
        application,
        trigger.getParent().getName());
    registry.counter(
        "trigger.errors.accessdenied",
        "application",
        application,
        "user",
        user,
        "pipeline",
        trigger.getParent().getName());
  }
  return hasPermission;
}
 
Example #10
Source File: PluginInfoService.java    From front50 with Apache License 2.0 5 votes vote down vote up
private void cleanupPreferredReleases(PluginInfo pluginInfo, PluginInfo.Release release) {
  if (release.isPreferred()) {
    Instant now = Instant.now();
    String user = AuthenticatedRequest.getSpinnakerUser().orElse("anonymous");

    pluginInfo.getReleases().stream()
        .filter(it -> !it.getVersion().equals(release.getVersion()))
        .forEach(
            it -> {
              it.setPreferred(false);
              it.setLastModified(now);
              it.setLastModifiedBy(user);
            });
  }
}
 
Example #11
Source File: PluginInfoService.java    From front50 with Apache License 2.0 5 votes vote down vote up
public PluginInfo createRelease(@Nonnull String id, @Nonnull PluginInfo.Release release) {
  release.setLastModifiedBy(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"));
  release.setLastModified(Instant.now());

  PluginInfo pluginInfo = repository.findById(id);
  pluginInfo.getReleases().add(release);
  cleanupPreferredReleases(pluginInfo, release);

  validate(pluginInfo);
  repository.update(pluginInfo.getId(), pluginInfo);
  return pluginInfo;
}
 
Example #12
Source File: AzureStorageService.java    From front50 with Apache License 2.0 5 votes vote down vote up
@Override
public <T extends Timestamped> void storeObject(ObjectType objectType, String objectKey, T item) {

  String key = buildKeyPath(objectType.group, objectKey, objectType.defaultMetadataFilename);
  try {
    item.setLastModifiedBy(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"));
    byte[] bytes = objectMapper.writeValueAsBytes(item);

    CloudBlockBlob blob = this.getBlobContainer().getBlockBlobReference(key);
    if (blob.exists() && supportsVersioning()) {
      blob.createSnapshot();
    }
    blob.uploadFromByteArray(bytes, 0, bytes.length);
    writeLastModified(objectType.group);
    log.info(
        "{} object {} for  has been successfully uploaded.",
        value("group", objectType.group),
        value("key", key));
  } catch (StorageException se) {
    logStorageException(se, key);
  } catch (Exception e) {
    log.error(
        "Error encountered attempting to store {}: {}",
        value("key", key),
        value("exception", e.getMessage()));
  }
}
 
Example #13
Source File: BuildEventHandler.java    From echo with Apache License 2.0 5 votes vote down vote up
private Map getPropertiesFromEvent(BuildEvent event, Trigger trigger) {
  if (buildInfoService.isPresent()) {
    try {
      return AuthenticatedRequest.propagate(
              () -> buildInfoService.get().getProperties(event, trigger.getPropertyFile()),
              getKorkUser(trigger))
          .call();
    } catch (Exception e) {
      log.warn("Unable to get artifacts from event {}, trigger {}", event, trigger, e);
    }
  }
  return Collections.emptyMap();
}
 
Example #14
Source File: BuildEventHandler.java    From echo with Apache License 2.0 5 votes vote down vote up
protected List<Artifact> getArtifactsFromEvent(BuildEvent event, Trigger trigger) {
  if (buildInfoService.isPresent()) {
    try {
      return AuthenticatedRequest.propagate(
              () -> buildInfoService.get().getArtifactsFromBuildEvent(event, trigger),
              getKorkUser(trigger))
          .call();
    } catch (Exception e) {
      log.warn("Unable to get artifacts from event {}, trigger {}", event, trigger, e);
    }
  }
  return Collections.emptyList();
}
 
Example #15
Source File: BuildEventHandler.java    From echo with Apache License 2.0 5 votes vote down vote up
@Override
public Function<Trigger, Trigger> buildTrigger(BuildEvent buildEvent) {
  return inputTrigger -> {
    Trigger trigger =
        inputTrigger
            .atBuildNumber(buildEvent.getBuildNumber())
            .withEventId(buildEvent.getEventId())
            .withLink(buildEvent.getContent().getProject().getLastBuild().getUrl());
    if (buildInfoService.isPresent()) {
      try {
        return AuthenticatedRequest.propagate(
                () ->
                    trigger
                        .withBuildInfo(buildInfoService.get().getBuildInfo(buildEvent))
                        .withProperties(
                            buildInfoService
                                .get()
                                .getProperties(buildEvent, inputTrigger.getPropertyFile())),
                getKorkUser(trigger))
            .call();
      } catch (Exception e) {
        log.warn("Unable to add buildInfo and properties to trigger {}", trigger, e);
      }
    }
    return trigger;
  };
}
 
Example #16
Source File: PipelineCache.java    From echo with Apache License 2.0 5 votes vote down vote up
/**
 * If the pipeline is a v2 pipeline, plan that pipeline. Returns an empty map if the plan fails,
 * so that the pipeline is skipped.
 */
private Map<String, Object> planPipelineIfNeeded(
    Map<String, Object> pipeline, Predicate<Map<String, Object>> isV2Pipeline) {
  if (isV2Pipeline.test(pipeline)) {
    try {
      return AuthenticatedRequest.allowAnonymous(() -> orca.v2Plan(pipeline));
    } catch (Exception e) {
      // Don't fail the entire cache cycle if we fail a plan.
      log.error("Caught exception while planning templated pipeline: {}", pipeline, e);
      return Collections.emptyMap();
    }
  } else {
    return pipeline;
  }
}
 
Example #17
Source File: PipelineInitiator.java    From echo with Apache License 2.0 5 votes vote down vote up
private void triggerPipeline(Pipeline pipeline, TriggerSource triggerSource)
    throws RejectedExecutionException {
  Callable<Void> triggerWithCapturedContext =
      AuthenticatedRequest.propagate(() -> triggerPipelineImpl(pipeline, triggerSource));

  executorService.submit(triggerWithCapturedContext);
}
 
Example #18
Source File: FiatAuthenticationFilter.java    From fiat with Apache License 2.0 5 votes vote down vote up
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
  if (!fiatStatus.isEnabled()) {
    chain.doFilter(request, response);
    return;
  }

  Authentication auth =
      AuthenticatedRequest.getSpinnakerUser()
          .map(
              username ->
                  (Authentication)
                      new PreAuthenticatedAuthenticationToken(username, null, new ArrayList<>()))
          .orElseGet(
              () ->
                  new AnonymousAuthenticationToken(
                      "anonymous",
                      "anonymous",
                      AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));

  val ctx = SecurityContextHolder.createEmptyContext();
  ctx.setAuthentication(auth);
  SecurityContextHolder.setContext(ctx);
  log.debug("Set SecurityContext to user: {}", auth.getPrincipal().toString());
  chain.doFilter(request, response);
}
 
Example #19
Source File: FiatPermissionEvaluator.java    From fiat with Apache License 2.0 5 votes vote down vote up
private UserPermission.View buildFallbackView() {
  return new UserPermission.View(
          new UserPermission()
              .setId(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"))
              .setAccounts(
                  Arrays.stream(AuthenticatedRequest.getSpinnakerAccounts().orElse("").split(","))
                      .filter(a -> a != null && !a.isEmpty())
                      .map(a -> new Account().setName(a))
                      .collect(Collectors.toSet())))
      .setLegacyFallback(true)
      .setAllowAccessToUnknownApplications(true);
}
 
Example #20
Source File: FiatPermissionEvaluator.java    From fiat with Apache License 2.0 5 votes vote down vote up
public boolean canCreate(String resourceType, Object resource) {
  if (!fiatStatus.isEnabled()) {
    return true;
  }

  String username = getUsername(SecurityContextHolder.getContext().getAuthentication());

  try {
    return AuthenticatedRequest.propagate(
            () -> {
              return retryHandler.retry(
                  "determine whether " + username + " can create resource " + resource,
                  () -> {
                    try {
                      fiatService.canCreate(username, resourceType, resource);
                      return true;
                    } catch (RetrofitError re) {
                      boolean shouldRetry = true;
                      if (re.getKind() == RetrofitError.Kind.HTTP) {
                        switch (HttpStatus.valueOf(re.getResponse().getStatus())) {
                          case NOT_FOUND:
                            return false;
                          case BAD_REQUEST:
                            shouldRetry = false;
                        }
                      }
                      IntegrationException ie = new IntegrationException(re);
                      ie.setRetryable(shouldRetry);
                      throw ie;
                    }
                  });
            })
        .call();
  } catch (Exception e) {
    log.info(e.toString());
    return false;
  }
}
 
Example #21
Source File: ConfigBinStorageService.java    From kayenta with Apache License 2.0 5 votes vote down vote up
@Override
public List<Map<String, Object>> listObjectKeys(
    String accountName, ObjectType objectType, List<String> applications, boolean skipIndex) {
  ConfigBinNamedAccountCredentials credentials =
      accountCredentialsRepository.getRequiredOne(accountName);

  if (!skipIndex && objectType == ObjectType.CANARY_CONFIG) {
    Set<Map<String, Object>> canaryConfigSet =
        canaryConfigIndex.getCanaryConfigSummarySet(credentials, applications);

    return Lists.newArrayList(canaryConfigSet);
  } else {
    String ownerApp = credentials.getOwnerApp();
    String configType = credentials.getConfigType();
    ConfigBinRemoteService remoteService = credentials.getRemoteService();
    String jsonBody =
        AuthenticatedRequest.allowAnonymous(
            () ->
                retry.retry(
                    () -> remoteService.list(ownerApp, configType), MAX_RETRIES, RETRY_BACKOFF));

    try {
      List<String> ids =
          kayentaObjectMapper.readValue(jsonBody, new TypeReference<List<String>>() {});

      if (ids.size() > 0) {
        return ids.stream().map(i -> metadataFor(credentials, i)).collect(Collectors.toList());
      }
    } catch (IOException e) {
      log.error("List failed on path {}: {}", ownerApp, e);
    }

    return Collections.emptyList();
  }
}
 
Example #22
Source File: ConfigBinStorageService.java    From kayenta with Apache License 2.0 4 votes vote down vote up
@Override
public void deleteObject(String accountName, ObjectType objectType, String objectKey) {
  ConfigBinNamedAccountCredentials credentials =
      accountCredentialsRepository.getRequiredOne(accountName);
  String ownerApp = credentials.getOwnerApp();
  String configType = credentials.getConfigType();

  long updatedTimestamp = -1;
  String correlationId = null;
  String canaryConfigSummaryJson = null;

  if (objectType == ObjectType.CANARY_CONFIG) {
    updatedTimestamp = canaryConfigIndex.getRedisTime();

    Map<String, Object> existingCanaryConfigSummary =
        canaryConfigIndex.getSummaryFromId(credentials, objectKey);

    if (existingCanaryConfigSummary != null) {
      String canaryConfigName = (String) existingCanaryConfigSummary.get("name");
      List<String> applications = (List<String>) existingCanaryConfigSummary.get("applications");

      correlationId = UUID.randomUUID().toString();

      Map<String, Object> canaryConfigSummary =
          new ImmutableMap.Builder<String, Object>()
              .put("id", objectKey)
              .put("name", canaryConfigName)
              .put("updatedTimestamp", updatedTimestamp)
              .put("updatedTimestampIso", Instant.ofEpochMilli(updatedTimestamp).toString())
              .put("applications", applications)
              .build();

      try {
        canaryConfigSummaryJson = kayentaObjectMapper.writeValueAsString(canaryConfigSummary);
      } catch (JsonProcessingException e) {
        throw new IllegalArgumentException(
            "Problem serializing canaryConfigSummary -> " + canaryConfigSummary, e);
      }

      canaryConfigIndex.startPendingUpdate(
          credentials,
          updatedTimestamp + "",
          CanaryConfigIndexAction.DELETE,
          correlationId,
          canaryConfigSummaryJson);
    }
  }

  ConfigBinRemoteService remoteService = credentials.getRemoteService();

  // TODO(mgraff): If remoteService.delete() throws an exception when the target config does not
  // exist, we should
  // try/catch it here and then call canaryConfigIndex.removeFailedPendingUpdate() like the other
  // storage service
  // implementations do.
  AuthenticatedRequest.allowAnonymous(
      () ->
          retry.retry(
              () -> remoteService.delete(ownerApp, configType, objectKey),
              MAX_RETRIES,
              RETRY_BACKOFF));

  if (correlationId != null) {
    canaryConfigIndex.finishPendingUpdate(
        credentials, CanaryConfigIndexAction.DELETE, correlationId);
  }
}
 
Example #23
Source File: PipelineCache.java    From echo with Apache License 2.0 4 votes vote down vote up
private List<Map<String, Object>> fetchRawPipelines() {
  List<Map<String, Object>> rawPipelines =
      AuthenticatedRequest.allowAnonymous(() -> front50.getPipelines());
  return (rawPipelines == null) ? Collections.emptyList() : rawPipelines;
}
 
Example #24
Source File: AuthenticatedRequestContext.java    From kork with Apache License 2.0 4 votes vote down vote up
@Override
public Optional<String> get(String header) {
  return AuthenticatedRequest.get(header);
}
 
Example #25
Source File: AuthenticatedRequestContext.java    From kork with Apache License 2.0 4 votes vote down vote up
@Override
public void set(String header, String value) {
  AuthenticatedRequest.set(header, value);
}
 
Example #26
Source File: AuthenticatedRequestContext.java    From kork with Apache License 2.0 4 votes vote down vote up
@Override
public void clear() {
  AuthenticatedRequest.clear();
}
 
Example #27
Source File: AuthorizeController.java    From fiat with Apache License 2.0 4 votes vote down vote up
private Optional<UserPermission> getUserPermissionOrDefault(String userId) {
  String authenticatedUserId = AuthenticatedRequest.getSpinnakerUser().orElse(null);

  UserPermission userPermission =
      permissionsRepository.get(ControllerSupport.convert(userId)).orElse(null);

  if (userPermission != null) {
    registry
        .counter(getUserPermissionCounterId.withTag("success", true).withTag("fallback", false))
        .increment();
    return Optional.of(userPermission);
  }

  /*
   * User does not have any stored permissions but the requested userId matches the
   * X-SPINNAKER-USER header value, likely a request that has not transited gate.
   */
  if (userId.equalsIgnoreCase(authenticatedUserId)) {

    /*
     * First, attempt to resolve via the permissionsResolver.
     */
    if (configProps.isAllowPermissionResolverFallback()) {
      UserPermission resolvedUserPermission = permissionsResolver.resolve(authenticatedUserId);
      if (resolvedUserPermission.getAllResources().stream().anyMatch(Objects::nonNull)) {
        log.debug("Resolved fallback permissions for user {}", authenticatedUserId);
        userPermission = resolvedUserPermission;
      }
    }

    /*
     * If user permissions are not resolved, default to those of the unrestricted user.
     */
    if (userPermission == null && configProps.isDefaultToUnrestrictedUser()) {
      log.debug("Falling back to unrestricted user permissions for user {}", authenticatedUserId);
      userPermission =
          permissionsRepository
              .get(UnrestrictedResourceConfig.UNRESTRICTED_USERNAME)
              .map(u -> u.setId(authenticatedUserId))
              .orElse(null);
    }
  }

  log.debug(
      "Returning fallback permissions (user: {}, accounts: {}, roles: {})",
      userId,
      (userPermission != null) ? userPermission.getAccounts() : Collections.emptyList(),
      (userPermission != null)
          ? userPermission.getRoles().stream().map(Role::getName).collect(Collectors.toList())
          : Collections.emptyList());

  registry
      .counter(
          getUserPermissionCounterId
              .withTag("success", userPermission != null)
              .withTag("fallback", true))
      .increment();

  return Optional.ofNullable(userPermission);
}
 
Example #28
Source File: StorageServiceSupport.java    From front50 with Apache License 2.0 4 votes vote down vote up
public void update(String id, T item) {
  item.setLastModifiedBy(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"));
  item.setLastModified(System.currentTimeMillis());
  service.storeObject(objectType, buildObjectKey(id), item);
}
 
Example #29
Source File: StorageServiceSupport.java    From front50 with Apache License 2.0 4 votes vote down vote up
public void bulkImport(Collection<T> items) {
  User authenticatedUser = new User();
  authenticatedUser.setUsername(AuthenticatedRequest.getSpinnakerUser().orElse("anonymous"));

  if (service instanceof BulkStorageService) {
    String lastModifiedBy = AuthenticatedRequest.getSpinnakerUser().orElse("anonymous");
    Long lastModified = System.currentTimeMillis();

    items.forEach(
        item -> {
          item.setLastModifiedBy(lastModifiedBy);
          item.setLastModified(lastModified);
        });

    ((BulkStorageService) service).storeObjects(objectType, items);
    return;
  }

  Observable.from(items)
      .buffer(10)
      .flatMap(
          itemSet ->
              Observable.from(itemSet)
                  .flatMap(
                      item -> {
                        try {
                          return AuthenticatedRequest.propagate(
                                  () -> {
                                    update(item.getId(), item);
                                    return Observable.just(item);
                                  },
                                  true,
                                  authenticatedUser)
                              .call();
                        } catch (Exception e) {
                          throw new RuntimeException(e);
                        }
                      })
                  .subscribeOn(scheduler))
      .subscribeOn(scheduler)
      .toList()
      .toBlocking()
      .single();
}