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

be.objectify.deadbolt.java.ConstraintLogic Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/*
 * Copyright 2010-2016 Steve Chaloner
 *
 * 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 be.objectify.deadbolt.java;

import be.objectify.deadbolt.java.cache.PatternCache;
import be.objectify.deadbolt.java.cache.SubjectCache;
import be.objectify.deadbolt.java.models.PatternType;
import be.objectify.deadbolt.java.models.Subject;
import be.objectify.deadbolt.java.utils.TriFunction;
import javax.inject.Singleton;
import play.libs.concurrent.HttpExecution;
import play.mvc.Http;
import scala.concurrent.ExecutionContext;
import scala.concurrent.ExecutionContextExecutor;

import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * The logic behind the constraints.
 *
 * @author Steve Chaloner ([email protected])
 */
@Singleton
public class ConstraintLogic
{
    private final DeadboltAnalyzer analyzer;

    private final SubjectCache subjectCache;

    private final PatternCache patternCache;

    private final DeadboltExecutionContextProvider executionContextProvider;

    @Inject
    public ConstraintLogic(final DeadboltAnalyzer analyzer,
                           final SubjectCache subjectCache,
                           final PatternCache patternCache,
                           final ExecutionContextProvider ecProvider)
    {
        this.analyzer = analyzer;
        this.subjectCache = subjectCache;
        this.patternCache = patternCache;
        this.executionContextProvider = ecProvider.get();
    }

    public  CompletionStage subjectPresent(final Http.Context ctx,
                                                 final DeadboltHandler deadboltHandler,
                                                 final Optional content,
                                                 final TriFunction, CompletionStage> present,
                                                 final TriFunction, CompletionStage> notPresent,
                                                 final ConstraintPoint constraintPoint)
    {
        return subjectTest(ctx,
                           deadboltHandler,
                           content,
                           (context, handler, cnt) ->
                           {
                               handler.onAuthSuccess(context,
                                                     "subjectPresent",
                                                     constraintPoint);
                               return present.apply(context,
                                                    handler,
                                                    cnt);
                           },
                           notPresent);
    }

    public  CompletionStage subjectNotPresent(final Http.Context ctx,
                                                    final DeadboltHandler deadboltHandler,
                                                    final Optional content,
                                                    final TriFunction, CompletionStage> present,
                                                    final TriFunction, CompletionStage> notPresent,
                                                    final ConstraintPoint constraintPoint)
    {
        return subjectTest(ctx,
                           deadboltHandler,
                           content,
                           present,
                           (context, handler, cnt) ->
                           {
                               handler.onAuthSuccess(context,
                                                     "subjectNotPresent",
                                                     constraintPoint);
                               return notPresent.apply(context,
                                                    handler,
                                                    cnt);
                           });
    }

    private  CompletionStage subjectTest(final Http.Context ctx,
                                               final DeadboltHandler deadboltHandler,
                                               final Optional content,
                                               final TriFunction, CompletionStage> present,
                                               final TriFunction, CompletionStage> notPresent)
    {
        final ExecutionContextExecutor executor = executor();
        return getSubject(ctx,
                          deadboltHandler)
                .thenComposeAsync(maybeSubject -> maybeSubject.map(subject -> present.apply(ctx,
                                                                                            deadboltHandler,
                                                                                            content))
                                                              .orElseGet(() -> notPresent.apply(ctx,
                                                                                                deadboltHandler,
                                                                                                content)),
                                  executor);
    }

    public  CompletionStage restrict(final Http.Context ctx,
                                           final DeadboltHandler deadboltHandler,
                                           final Optional content,
                                           final Supplier> roleGroupSupplier,
                                           final Function> pass,
                                           final TriFunction, CompletionStage> fail,
                                           final ConstraintPoint constraintPoint)
    {
        final ExecutionContextExecutor executor = executor();
        return getSubject(ctx,
                          deadboltHandler)
                .thenApplyAsync(subjectOption ->
                                {
                                    boolean roleOk = false;
                                    if (subjectOption.isPresent())
                                    {
                                        final List roleGroups = roleGroupSupplier.get();
                                        for (int i = 0; !roleOk && i < roleGroups.size(); i++)
                                        {
                                            roleOk = analyzer.checkRole(subjectOption,
                                                                        roleGroups.get(i));
                                        }
                                    }
                                    return roleOk;
                                },
                                executor)
                .thenComposeAsync(allowed -> allowed ? pass(ctx,
                                                            deadboltHandler,
                                                            pass,
                                                            constraintPoint,
                                                            "restrict")
                                                     : fail.apply(ctx,
                                                                  deadboltHandler,
                                                                  content),
                                  executor);

    }

    public  CompletionStage roleBasedPermissions(final Http.Context ctx,
                                                       final DeadboltHandler deadboltHandler,
                                                       final Optional content,
                                                       final String roleName,
                                                       final Function> pass,
                                                       final TriFunction, CompletionStage> fail,
                                                       final ConstraintPoint constraintPoint)
    {
        final ExecutionContextExecutor executor = executor();
        return getSubject(ctx,
                          deadboltHandler)
                .thenComposeAsync(maybeSubject -> maybeSubject.isPresent() ? deadboltHandler.getPermissionsForRole(roleName)
                                                                                            .thenApplyAsync(permissions -> permissions.stream()
                                                                                                                                      .map(permission -> Optional.ofNullable(patternCache.apply(permission.getValue())))
                                                                                                                                      .map(maybePattern -> analyzer.checkRegexPattern(maybeSubject,
                                                                                                                                                                                      maybePattern))
                                                                                                                                      .filter(matches -> matches)
                                                                                                                                      .findFirst()
                                                                                                                                      .isPresent(),
                                                                                                            executor)

                                                                           : CompletableFuture.completedFuture(false),
                                  executor)
                .thenComposeAsync(allowed -> allowed ? pass(ctx,
                                                            deadboltHandler,
                                                            pass,
                                                            constraintPoint,
                                                            "roleBasedPermissions")
                                                     : fail.apply(ctx,
                                                                  deadboltHandler,
                                                                  content),
                                  executor);

    }

    public  CompletionStage pattern(final Http.Context ctx,
                                          final DeadboltHandler deadboltHandler,
                                          final Optional content,
                                          final String value,
                                          final PatternType patternType,
                                          final Optional meta,
                                          final boolean invert,
                                          final Function> pass,
                                          final TriFunction, CompletionStage> fail,
                                          final ConstraintPoint constraintPoint)
    {
        final CompletionStage result;

        switch (patternType)
        {
            case EQUALITY:
                result = equality(ctx,
                                  deadboltHandler,
                                  content,
                                  value,
                                  invert,
                                  pass,
                                  fail,
                                  constraintPoint);
                break;
            case REGEX:
                result = regex(ctx,
                               deadboltHandler,
                               content,
                               value,
                               invert,
                               pass,
                               fail,
                               constraintPoint);
                break;
            case CUSTOM:
                result = custom(ctx,
                                deadboltHandler,
                                content,
                                value,
                                meta,
                                invert,
                                pass,
                                fail,
                                constraintPoint);
                break;
            default:
                throw new RuntimeException("Unknown pattern type: " + patternType);
        }

        return result;
    }

    public  CompletionStage dynamic(final Http.Context ctx,
                                          final DeadboltHandler deadboltHandler,
                                          final Optional content,
                                          final String name,
                                          final Optional meta,
                                          final Function> pass,
                                          final TriFunction, CompletionStage> fail,
                                          final ConstraintPoint constraintPoint)
    {
        final ExecutionContextExecutor executor = executor();
        return deadboltHandler.getDynamicResourceHandler(ctx)
                              .thenApplyAsync(option -> option.orElseGet(() -> ExceptionThrowingDynamicResourceHandler.INSTANCE),
                                              executor)
                              .thenComposeAsync(drh -> drh.isAllowed(name,
                                                                     meta,
                                                                     deadboltHandler,
                                                                     ctx),
                                                executor)
                              .thenComposeAsync(allowed -> allowed ? pass(ctx,
                                                                          deadboltHandler,
                                                                          pass,
                                                                          constraintPoint,
                                                                          "dynamic")
                                                                   : fail.apply(ctx,
                                                                                deadboltHandler,
                                                                                content),
                                                executor);
    }

    private  CompletionStage custom(final Http.Context ctx,
                                          final DeadboltHandler deadboltHandler,
                                          final Optional content,
                                          final String value,
                                          final Optional meta,
                                          final boolean invert,
                                          final Function> pass,
                                          final TriFunction, CompletionStage> fail,
                                          final ConstraintPoint constraintPoint)
    {
        ctx.args.put(ConfigKeys.PATTERN_INVERT,
                     invert);
        final ExecutionContextExecutor executor = executor();
        return deadboltHandler.getDynamicResourceHandler(ctx)
                              .thenApplyAsync(option -> option.orElseGet(() -> ExceptionThrowingDynamicResourceHandler.INSTANCE),
                                              executor)
                              .thenComposeAsync(resourceHandler -> resourceHandler.checkPermission(value,
                                                                                                   meta,
                                                                                                   deadboltHandler,
                                                                                                   ctx),
                                                executor)
                              .thenComposeAsync(allowed -> (invert ? !allowed : allowed) ? pass(ctx,
                                                                                                deadboltHandler,
                                                                                                pass,
                                                                                                constraintPoint,
                                                                                                "pattern - custom")
                                                                                         : fail.apply(ctx,
                                                                                                      deadboltHandler,
                                                                                                      content),
                                                executor);
    }

    private  CompletionStage equality(final Http.Context ctx,
                                            final DeadboltHandler deadboltHandler,
                                            final Optional content,
                                            final String value,
                                            final boolean invert,
                                            final Function> pass,
                                            final TriFunction, CompletionStage> fail,
                                            final ConstraintPoint constraintPoint)
    {
        final ExecutionContextExecutor executor = executor();
        return getSubject(ctx,
                          deadboltHandler)
                .thenApplyAsync(subject -> subject.isPresent() ? analyzer.checkPatternEquality(subject,
                                                                                               Optional.ofNullable(value))
                                                               : invert, // this is a little clumsy - it means no subject + invert is still denied
                                executor)
                .thenComposeAsync(equal -> (invert ? !equal : equal) ? pass(ctx,
                                                                            deadboltHandler,
                                                                            pass,
                                                                            constraintPoint,
                                                                            "pattern - equality")
                                                                     : fail.apply(ctx,
                                                                                  deadboltHandler,
                                                                                  content), executor);
    }

    protected CompletionStage> getSubject(final Http.Context ctx,
                                                                      final DeadboltHandler deadboltHandler)
    {
        return subjectCache.apply(deadboltHandler,
                                  ctx);
    }

    /**
     * Checks access to the resource based on the regex
     *
     * @param ctx             the HTTP context
     * @param deadboltHandler the Deadbolt handler
     * @param invert          if true, invert the application of the constraint
     * @return the necessary result
     */
    private  CompletionStage regex(final Http.Context ctx,
                                         final DeadboltHandler deadboltHandler,
                                         final Optional content,
                                         final String value,
                                         final boolean invert,
                                         final Function> pass,
                                         final TriFunction, CompletionStage> fail,
                                         final ConstraintPoint constraintPoint)
    {
        final ExecutionContextExecutor executor = executor();
        return CompletableFuture.supplyAsync(() -> patternCache.apply(value),
                                             executor)
                                .thenCombineAsync(getSubject(ctx,
                                                             deadboltHandler),
                                                  (patternValue, subject) ->
                                                          subject.isPresent() ? analyzer.checkRegexPattern(subject,
                                                                                                           Optional.ofNullable(patternValue))
                                                                              : invert, // this is a little clumsy - it means no subject + invert is still denied
                                                  executor)

                                .thenComposeAsync(hasPassed -> (invert ? !hasPassed : hasPassed) ? pass(ctx,
                                                                                                        deadboltHandler,
                                                                                                        pass,
                                                                                                        constraintPoint,
                                                                                                        "pattern - regex")
                                                                                                 : fail.apply(ctx,
                                                                                                              deadboltHandler,
                                                                                                              content),
                                                  executor);
    }


    protected ExecutionContextExecutor executor()
    {
        final ExecutionContext executionContext = executionContextProvider.get();
        return HttpExecution.fromThread(executionContext);
    }

    private  CompletionStage pass(final Http.Context context,
                                        final DeadboltHandler handler,
                                        final Function> pass,
                                        final ConstraintPoint constraintPoint,
                                        final String constraintType)
    {
        handler.onAuthSuccess(context,
                              constraintType,
                              constraintPoint);
        return pass.apply(context);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy