001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.graphql.server.util;
018
019import com.echothree.model.data.user.common.pk.UserVisitPK;
020import com.echothree.util.common.form.BaseForm;
021import com.echothree.util.server.control.GraphQlSecurityCommand;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.InvocationTargetException;
024
025public interface GraphQlSecurityUtils {
026
027    private static Constructor<?> findConstructor(final Class<? extends GraphQlSecurityCommand> command) {
028        Constructor<?> foundCtor = null;
029        var allConstructors = command.getDeclaredConstructors();
030
031        // Search all available Constructors for one that has a UserVisitPK as the
032        // first parameter, and a type assignable to BaseForm as the second parameter.
033        for(var ctor : allConstructors) {
034            var pType = ctor.getParameterTypes();
035
036            if(pType.length == 2) {
037                if(pType[0] == UserVisitPK.class && BaseForm.class.isAssignableFrom(pType[1])) {
038                    foundCtor = ctor;
039                    break;
040                }
041            }
042        }
043
044        if(foundCtor == null) {
045            throw new RuntimeException("command does not implement the required Constructor");
046        }
047
048        return foundCtor;
049    }
050
051    static boolean hasAccess(final GraphQlExecutionContext context, final Class<? extends GraphQlSecurityCommand> command, final BaseForm form) {
052        boolean hasAccess;
053
054        try {
055            var ctor = findConstructor(command); // Search for the Constructor that's required here
056            var commandInstance = ctor.newInstance(context.getUserVisitPK(), form);
057            var graphQlSecurityCommand = (GraphQlSecurityCommand) commandInstance;
058
059            // Execute the instantiated command's security check function for the current user.
060            graphQlSecurityCommand.security();
061
062            // User will have access to this GraphQL field as long as there are no security
063            // messages (errors) generated by the command.
064            hasAccess = !graphQlSecurityCommand.hasSecurityMessages();
065        } catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
066            throw new RuntimeException(e);
067        }
068
069        return hasAccess;
070    }
071    
072}