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}