Search in sources :

Example 1 with UserPermission

use of com.netflix.spinnaker.fiat.model.UserPermission in project fiat by spinnaker.

the class RolesController method putUserPermission.

@RequestMapping(value = "/{userId:.+}", method = RequestMethod.PUT)
public void putUserPermission(@PathVariable String userId, @RequestBody @NonNull List<String> externalRoles) {
    List<Role> convertedRoles = externalRoles.stream().map(extRole -> new Role().setSource(Role.Source.EXTERNAL).setName(extRole)).collect(Collectors.toList());
    ExternalUser extUser = new ExternalUser().setId(ControllerSupport.convert(userId)).setExternalRoles(convertedRoles);
    try {
        UserPermission userPermission = permissionsResolver.resolveAndMerge(extUser);
        log.debug("Updated user permissions (userId: {}, roles: {}, suppliedExternalRoles: {})", userId, userPermission.getRoles().stream().map(Role::getName).collect(Collectors.toList()), externalRoles);
        permissionsRepository.put(userPermission);
    } catch (PermissionResolutionException pre) {
        throw new UserPermissionModificationException(pre);
    }
}
Also used : Role(com.netflix.spinnaker.fiat.model.resources.Role) ExternalUser(com.netflix.spinnaker.fiat.permissions.ExternalUser) PathVariable(org.springframework.web.bind.annotation.PathVariable) PermissionResolutionException(com.netflix.spinnaker.fiat.permissions.PermissionResolutionException) Setter(lombok.Setter) NonNull(lombok.NonNull) PermissionsRepository(com.netflix.spinnaker.fiat.permissions.PermissionsRepository) HttpServletResponse(javax.servlet.http.HttpServletResponse) Autowired(org.springframework.beans.factory.annotation.Autowired) RequestMapping(org.springframework.web.bind.annotation.RequestMapping) RequestMethod(org.springframework.web.bind.annotation.RequestMethod) IOException(java.io.IOException) PermissionsResolver(com.netflix.spinnaker.fiat.permissions.PermissionsResolver) Collectors(java.util.stream.Collectors) RestController(org.springframework.web.bind.annotation.RestController) RequestBody(org.springframework.web.bind.annotation.RequestBody) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) ConditionalOnExpression(org.springframework.boot.autoconfigure.condition.ConditionalOnExpression) UserRolesSyncer(com.netflix.spinnaker.fiat.roles.UserRolesSyncer) Role(com.netflix.spinnaker.fiat.model.resources.Role) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission) PermissionResolutionException(com.netflix.spinnaker.fiat.permissions.PermissionResolutionException) ExternalUser(com.netflix.spinnaker.fiat.permissions.ExternalUser) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 2 with UserPermission

use of com.netflix.spinnaker.fiat.model.UserPermission in project fiat by spinnaker.

the class RedisPermissionsRepository method getUnrestrictedUserPermission.

private UserPermission getUnrestrictedUserPermission() {
    String serverLastModified = NO_LAST_MODIFIED;
    byte[] bServerLastModified = redisRead(new TimeoutContext("checkLastModified", clock, configProps.getRepository().getCheckLastModifiedTimeout()), c -> c.get(SafeEncoder.encode(unrestrictedLastModifiedKey())));
    if (bServerLastModified == null || bServerLastModified.length == 0) {
        log.debug("no last modified time available in redis for user {} using default of {}", UNRESTRICTED, NO_LAST_MODIFIED);
    } else {
        serverLastModified = SafeEncoder.encode(bServerLastModified);
    }
    try {
        UserPermission userPermission = unrestrictedPermission.get(serverLastModified);
        if (userPermission != null && !serverLastModified.equals(NO_LAST_MODIFIED)) {
            fallbackLastModified.set(serverLastModified);
        }
        return userPermission;
    } catch (Throwable ex) {
        log.error("failed reading user {} from cache for key {}", UNRESTRICTED, serverLastModified, ex);
        String fallback = fallbackLastModified.get();
        if (fallback != null) {
            UserPermission fallbackPermission = unrestrictedPermission.getIfPresent(fallback);
            if (fallbackPermission != null) {
                log.warn("serving fallback permission for user {} from key {} as {}", UNRESTRICTED, fallback, fallbackPermission);
                return fallbackPermission;
            }
            log.warn("no fallback entry remaining in cache for key {}", fallback);
        }
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        throw new IntegrationException(ex);
    }
}
Also used : IntegrationException(com.netflix.spinnaker.kork.exceptions.IntegrationException) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission)

Example 3 with UserPermission

use of com.netflix.spinnaker.fiat.model.UserPermission in project fiat by spinnaker.

the class RedisPermissionsRepository method getFromRedis.

private Optional<UserPermission> getFromRedis(@NonNull String id) {
    try {
        TimeoutContext timeoutContext = new TimeoutContext(String.format("getPermission for user: %s", id), clock, configProps.getRepository().getGetPermissionTimeout());
        boolean userExists = UNRESTRICTED.equals(id) || redisRead(timeoutContext, c -> c.sismember(allUsersKey, SafeEncoder.encode(id)));
        if (!userExists) {
            log.debug("request for user {} not found in redis", id);
            return Optional.empty();
        }
        UserPermission userPermission = new UserPermission().setId(id);
        for (Resource r : resources) {
            ResourceType resourceType = r.getResourceType();
            Map<String, Resource> resourcePermissions = getUserResourceMapFromRedis(id, resourceType);
            if (resourcePermissions != null && !resourcePermissions.isEmpty()) {
                userPermission.addResources(resourcePermissions.values());
            }
        }
        if (!UNRESTRICTED.equals(id)) {
            userPermission.setAdmin(redisRead(timeoutContext, c -> c.sismember(adminKey, SafeEncoder.encode(id))));
            userPermission.merge(getUnrestrictedUserPermission());
        }
        return Optional.of(userPermission);
    } catch (Throwable t) {
        String message = String.format("Storage exception reading %s entry.", id);
        log.error(message, t);
        if (t instanceof SpinnakerException) {
            throw (SpinnakerException) t;
        }
        throw new PermissionReadException(message, t);
    }
}
Also used : java.util(java.util) net.jpountz.lz4(net.jpountz.lz4) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) BinaryJedisCommands(redis.clients.jedis.commands.BinaryJedisCommands) SafeEncoder(redis.clients.jedis.util.SafeEncoder) redis.clients.jedis(redis.clients.jedis) Duration(java.time.Duration) TypeReference(com.fasterxml.jackson.core.type.TypeReference) RetryRegistry(io.github.resilience4j.retry.RetryRegistry) RedisClientDelegate(com.netflix.spinnaker.kork.jedis.RedisClientDelegate) Caffeine(com.github.benmanes.caffeine.cache.Caffeine) LoadingCache(com.github.benmanes.caffeine.cache.LoadingCache) NonNull(lombok.NonNull) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) ResourceType(com.netflix.spinnaker.fiat.model.resources.ResourceType) IOException(java.io.IOException) UnrestrictedResourceConfig(com.netflix.spinnaker.fiat.config.UnrestrictedResourceConfig) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) ExecutionException(java.util.concurrent.ExecutionException) Resource(com.netflix.spinnaker.fiat.model.resources.Resource) Slf4j(lombok.extern.slf4j.Slf4j) IntegrationException(com.netflix.spinnaker.kork.exceptions.IntegrationException) ForkJoinPool(java.util.concurrent.ForkJoinPool) Role(com.netflix.spinnaker.fiat.model.resources.Role) Clock(java.time.Clock) SpinnakerException(com.netflix.spinnaker.kork.exceptions.SpinnakerException) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission) SpinnakerException(com.netflix.spinnaker.kork.exceptions.SpinnakerException) Resource(com.netflix.spinnaker.fiat.model.resources.Resource) ResourceType(com.netflix.spinnaker.fiat.model.resources.ResourceType) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission)

Example 4 with UserPermission

use of com.netflix.spinnaker.fiat.model.UserPermission in project fiat by spinnaker.

the class RedisPermissionsRepository method put.

@Override
public RedisPermissionsRepository put(@NonNull UserPermission permission) {
    String userId = permission.getId();
    byte[] bUserId = SafeEncoder.encode(userId);
    List<ResourceType> resourceTypes = resources.stream().map(Resource::getResourceType).collect(Collectors.toList());
    Map<ResourceType, Map<String, Resource>> resourceTypeToRedisValue = new HashMap<>(resourceTypes.size());
    permission.getAllResources().forEach(resource -> {
        resourceTypeToRedisValue.computeIfAbsent(resource.getResourceType(), key -> new HashMap<>()).put(resource.getName(), resource);
    });
    try {
        Set<Role> existingRoles = new HashSet<>(getUserRoleMapFromRedis(userId).values());
        // These updates are pre-prepared to reduce work done during the multi-key pipeline
        List<PutUpdateData> updateData = new ArrayList<>();
        for (ResourceType rt : resourceTypes) {
            Map<String, Resource> redisValue = resourceTypeToRedisValue.get(rt);
            byte[] userResourceKey = userKey(userId, rt);
            PutUpdateData pud = new PutUpdateData();
            pud.userResourceKey = userResourceKey;
            if (redisValue == null || redisValue.size() == 0) {
                pud.compressedData = null;
            } else {
                pud.compressedData = lz4Compressor.compress(objectMapper.writeValueAsBytes(redisValue));
            }
            updateData.add(pud);
        }
        AtomicReference<Response<List<String>>> serverTime = new AtomicReference<>();
        redisClientDelegate.withMultiKeyPipeline(pipeline -> {
            if (permission.isAdmin()) {
                pipeline.sadd(adminKey, bUserId);
            } else {
                pipeline.srem(adminKey, bUserId);
            }
            permission.getRoles().forEach(role -> pipeline.sadd(roleKey(role), bUserId));
            existingRoles.stream().filter(it -> !permission.getRoles().contains(it)).forEach(role -> pipeline.srem(roleKey(role), bUserId));
            for (PutUpdateData pud : updateData) {
                if (pud.compressedData == null) {
                    pipeline.del(pud.userResourceKey);
                } else {
                    byte[] tempKey = SafeEncoder.encode(UUID.randomUUID().toString());
                    pipeline.set(tempKey, pud.compressedData);
                    pipeline.rename(tempKey, pud.userResourceKey);
                }
            }
            serverTime.set(pipeline.time());
            pipeline.sadd(allUsersKey, bUserId);
            pipeline.sync();
        });
        if (UNRESTRICTED.equals(userId)) {
            String lastModified = serverTime.get().get().get(0);
            redisClientDelegate.withCommandsClient(c -> {
                log.debug("set last modified for user {} to {}", UNRESTRICTED, lastModified);
                c.set(unrestrictedLastModifiedKey(), lastModified);
            });
        }
    } catch (Exception e) {
        log.error("Storage exception writing {} entry.", userId, e);
    }
    return this;
}
Also used : java.util(java.util) net.jpountz.lz4(net.jpountz.lz4) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) BinaryJedisCommands(redis.clients.jedis.commands.BinaryJedisCommands) SafeEncoder(redis.clients.jedis.util.SafeEncoder) redis.clients.jedis(redis.clients.jedis) Duration(java.time.Duration) TypeReference(com.fasterxml.jackson.core.type.TypeReference) RetryRegistry(io.github.resilience4j.retry.RetryRegistry) RedisClientDelegate(com.netflix.spinnaker.kork.jedis.RedisClientDelegate) Caffeine(com.github.benmanes.caffeine.cache.Caffeine) LoadingCache(com.github.benmanes.caffeine.cache.LoadingCache) NonNull(lombok.NonNull) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) ResourceType(com.netflix.spinnaker.fiat.model.resources.ResourceType) IOException(java.io.IOException) UnrestrictedResourceConfig(com.netflix.spinnaker.fiat.config.UnrestrictedResourceConfig) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) ExecutionException(java.util.concurrent.ExecutionException) Resource(com.netflix.spinnaker.fiat.model.resources.Resource) Slf4j(lombok.extern.slf4j.Slf4j) IntegrationException(com.netflix.spinnaker.kork.exceptions.IntegrationException) ForkJoinPool(java.util.concurrent.ForkJoinPool) Role(com.netflix.spinnaker.fiat.model.resources.Role) Clock(java.time.Clock) SpinnakerException(com.netflix.spinnaker.kork.exceptions.SpinnakerException) UserPermission(com.netflix.spinnaker.fiat.model.UserPermission) Resource(com.netflix.spinnaker.fiat.model.resources.Resource) ResourceType(com.netflix.spinnaker.fiat.model.resources.ResourceType) AtomicReference(java.util.concurrent.atomic.AtomicReference) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) IntegrationException(com.netflix.spinnaker.kork.exceptions.IntegrationException) SpinnakerException(com.netflix.spinnaker.kork.exceptions.SpinnakerException) Role(com.netflix.spinnaker.fiat.model.resources.Role)

Example 5 with UserPermission

use of com.netflix.spinnaker.fiat.model.UserPermission in project fiat by spinnaker.

the class AuthorizeController method getUserPermissionOrDefault.

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) && (configProps.isAllowPermissionResolverFallback() || configProps.isDefaultToUnrestrictedUser())) {
        Optional<UserPermission> unrestricted = permissionsRepository.get(UnrestrictedResourceConfig.UNRESTRICTED_USERNAME);
        if (!unrestricted.isPresent()) {
            log.error("Error resolving fallback permissions: lookup of unrestricted user failed. Access to anonymous resources will fail");
        }
        /*
       * First, attempt to resolve via the permissionsResolver.
       */
        if (configProps.isAllowPermissionResolverFallback()) {
            UserPermission resolvedUserPermission = permissionsResolver.resolve(ControllerSupport.convert(authenticatedUserId));
            if (resolvedUserPermission.getAllResources().stream().anyMatch(Objects::nonNull)) {
                log.debug("Resolved fallback permissions for user {}", authenticatedUserId);
                userPermission = resolvedUserPermission;
                unrestricted.ifPresent(userPermission::merge);
            }
        }
        /*
       * If user permissions are not resolved, default to those of the unrestricted user.
       */
        if (userPermission == null && configProps.isDefaultToUnrestrictedUser()) {
            if (unrestricted.isPresent()) {
                log.debug("Falling back to unrestricted user permissions for user {}", authenticatedUserId);
                userPermission = new UserPermission().setId(authenticatedUserId).merge(unrestricted.get());
            }
        }
        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());
    } else {
        log.debug("Not populating fallback. userId: {}, authenticatedUserId: {}, allowPermissionResolverFallback: {}, defaultToUnrestrictedUser: {}", userId, authenticatedUserId, configProps.isAllowPermissionResolverFallback(), configProps.isDefaultToUnrestrictedUser());
    }
    registry.counter(getUserPermissionCounterId.withTag("success", userPermission != null).withTag("fallback", true)).increment();
    return Optional.ofNullable(userPermission);
}
Also used : UserPermission(com.netflix.spinnaker.fiat.model.UserPermission)

Aggregations

UserPermission (com.netflix.spinnaker.fiat.model.UserPermission)6 Role (com.netflix.spinnaker.fiat.model.resources.Role)4 IntegrationException (com.netflix.spinnaker.kork.exceptions.IntegrationException)3 IOException (java.io.IOException)3 Collectors (java.util.stream.Collectors)3 NonNull (lombok.NonNull)3 Slf4j (lombok.extern.slf4j.Slf4j)3 TypeReference (com.fasterxml.jackson.core.type.TypeReference)2 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)2 Caffeine (com.github.benmanes.caffeine.cache.Caffeine)2 LoadingCache (com.github.benmanes.caffeine.cache.LoadingCache)2 UnrestrictedResourceConfig (com.netflix.spinnaker.fiat.config.UnrestrictedResourceConfig)2 Resource (com.netflix.spinnaker.fiat.model.resources.Resource)2 ResourceType (com.netflix.spinnaker.fiat.model.resources.ResourceType)2 PermissionResolutionException (com.netflix.spinnaker.fiat.permissions.PermissionResolutionException)2 SpinnakerException (com.netflix.spinnaker.kork.exceptions.SpinnakerException)2 RedisClientDelegate (com.netflix.spinnaker.kork.jedis.RedisClientDelegate)2 RetryRegistry (io.github.resilience4j.retry.RetryRegistry)2 Clock (java.time.Clock)2 Duration (java.time.Duration)2