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

org.projectnessie.server.authz.CelAccessChecker Maven / Gradle / Ivy

/*
 * Copyright (C) 2020 Dremio
 *
 * 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 org.projectnessie.server.authz;

import com.google.common.collect.ImmutableMap;
import java.security.AccessControlException;
import java.util.Map;
import java.util.function.Supplier;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.projectnessie.cel.tools.ScriptException;
import org.projectnessie.model.ContentsKey;
import org.projectnessie.server.config.QuarkusNessieAuthorizationConfig;
import org.projectnessie.services.authz.AccessChecker;
import org.projectnessie.services.authz.AccessContext;
import org.projectnessie.versioned.NamedRef;

/**
 * A reference implementation of the {@link AccessChecker} that performs access checks using CEL
 * expressions.
 */
@ApplicationScoped
public class CelAccessChecker implements AccessChecker {
  private final QuarkusNessieAuthorizationConfig config;
  private final CompiledAuthorizationRules compiledRules;

  public enum AuthorizationRuleType {
    VIEW_REFERENCE,
    CREATE_REFERENCE,
    DELETE_REFERENCE,
    LIST_COMMIT_LOG,
    READ_ENTRIES,
    ASSIGN_REFERENCE_TO_HASH,
    COMMIT_CHANGE_AGAINST_REFERENCE,
    READ_ENTITY_VALUE,
    UPDATE_ENTITY,
    DELETE_ENTITY;
  }

  @Inject
  public CelAccessChecker(
      QuarkusNessieAuthorizationConfig config, CompiledAuthorizationRules compiledRules) {
    this.config = config;
    this.compiledRules = compiledRules;
  }

  @Override
  public void canViewReference(AccessContext context, NamedRef ref) throws AccessControlException {
    canPerformOpOnReference(context, ref, AuthorizationRuleType.VIEW_REFERENCE);
  }

  @Override
  public void canCreateReference(AccessContext context, NamedRef ref)
      throws AccessControlException {
    canPerformOpOnReference(context, ref, AuthorizationRuleType.CREATE_REFERENCE);
  }

  @Override
  public void canAssignRefToHash(AccessContext context, NamedRef ref)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnReference(context, ref, AuthorizationRuleType.ASSIGN_REFERENCE_TO_HASH);
  }

  @Override
  public void canDeleteReference(AccessContext context, NamedRef ref)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnReference(context, ref, AuthorizationRuleType.DELETE_REFERENCE);
  }

  @Override
  public void canReadEntries(AccessContext context, NamedRef ref) throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnReference(context, ref, AuthorizationRuleType.READ_ENTRIES);
  }

  @Override
  public void canListCommitLog(AccessContext context, NamedRef ref) throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnReference(context, ref, AuthorizationRuleType.LIST_COMMIT_LOG);
  }

  @Override
  public void canCommitChangeAgainstReference(AccessContext context, NamedRef ref)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnReference(context, ref, AuthorizationRuleType.COMMIT_CHANGE_AGAINST_REFERENCE);
  }

  @Override
  public void canReadEntityValue(
      AccessContext context, NamedRef ref, ContentsKey key, String contentsId)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnPath(context, ref, key, AuthorizationRuleType.READ_ENTITY_VALUE);
  }

  @Override
  public void canUpdateEntity(
      AccessContext context, NamedRef ref, ContentsKey key, String contentsId)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnPath(context, ref, key, AuthorizationRuleType.UPDATE_ENTITY);
  }

  @Override
  public void canDeleteEntity(
      AccessContext context, NamedRef ref, ContentsKey key, String contentsId)
      throws AccessControlException {
    canViewReference(context, ref);
    canPerformOpOnPath(context, ref, key, AuthorizationRuleType.DELETE_ENTITY);
  }

  private String getRoleName(AccessContext context) {
    return null != context.user() ? context.user().getName() : "";
  }

  private void canPerformOpOnReference(
      AccessContext context, NamedRef ref, AuthorizationRuleType type) {
    if (!config.enabled()) {
      return;
    }
    String roleName = getRoleName(context);
    ImmutableMap arguments =
        ImmutableMap.of("ref", ref.getName(), "role", roleName, "op", type.name());

    Supplier errorMsgSupplier =
        () ->
            String.format(
                "'%s' is not allowed for role '%s' on reference '%s'",
                type, roleName, ref.getName());
    canPerformOp(arguments, errorMsgSupplier);
  }

  private void canPerformOpOnPath(
      AccessContext context, NamedRef ref, ContentsKey contentsKey, AuthorizationRuleType type) {
    if (!config.enabled()) {
      return;
    }
    String roleName = getRoleName(context);
    ImmutableMap arguments =
        ImmutableMap.of(
            "ref",
            ref.getName(),
            "path",
            contentsKey.toPathString(),
            "role",
            roleName,
            "op",
            type.name());

    Supplier errorMsgSupplier =
        () ->
            String.format(
                "'%s' is not allowed for role '%s' on content '%s'",
                type, roleName, contentsKey.toPathString());

    canPerformOp(arguments, errorMsgSupplier);
  }

  private void canPerformOp(Map arguments, Supplier errorMessageSupplier) {
    boolean allowed =
        compiledRules.getRules().entrySet().stream()
            .anyMatch(
                entry -> {
                  try {
                    return entry.getValue().execute(Boolean.class, arguments);
                  } catch (ScriptException e) {
                    throw new RuntimeException(
                        String.format(
                            "Failed to execute authorization rule with id '%s' and expression '%s' due to: %s",
                            entry.getKey(), entry.getValue(), e.getMessage()),
                        e);
                  }
                });
    if (!allowed) {
      throw new AccessControlException(errorMessageSupplier.get());
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy