001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 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// --------------------------------------------------------------------------------
016package com.echothree.model.control.graphql.server.util;
017
018import com.echothree.model.data.user.common.pk.UserVisitPK;
019import com.echothree.model.data.user.server.entity.UserSession;
020import com.echothree.model.data.user.server.entity.UserVisit;
021import com.echothree.util.common.form.BaseForm;
022import com.echothree.util.server.control.GraphQlSecurityCommand;
023import java.util.HashMap;
024import java.util.Map;
025import javax.enterprise.inject.spi.CDI;
026
027public class GraphQlExecutionContext {
028
029    private final UserVisitPK userVisitPK;
030    private final UserVisit userVisit;
031    private final UserSession userSession;
032    private final String remoteInet4Address;
033    
034    private final Map<Class<? extends GraphQlSecurityCommand<? extends BaseForm>>, Boolean> securityCache = new HashMap<>();
035
036    public GraphQlExecutionContext(UserVisitPK userVisitPK, UserVisit userVisit, UserSession userSession, String remoteInet4Address) {
037        this.userVisitPK = userVisitPK;
038        this.userVisit = userVisit;
039        this.userSession = userSession;
040        this.remoteInet4Address = remoteInet4Address;
041    }
042    
043    public UserVisitPK getUserVisitPK() {
044        return userVisitPK;
045    }
046    
047    public UserVisit getUserVisit() {
048        return userVisit;
049    }
050    
051    public UserSession getUserSession() {
052        return userSession;
053    }
054    
055    public String getRemoteInet4Address() {
056        return remoteInet4Address;
057    }
058
059    public <F extends BaseForm> boolean hasAccess(final Class<? extends GraphQlSecurityCommand<F>> command) {
060        return hasAccess(command, null);
061    }
062
063    public <F extends BaseForm> boolean hasAccess(final Class<? extends GraphQlSecurityCommand<F>> command, final F form) {
064        var canCache = form == null;
065        var hasAccess = canCache ? securityCache.get(command) : null; // Bypass if it cannot be cached.
066
067        if(hasAccess == null) {
068            hasAccess = hasAccess(this, command, form);
069
070            if(canCache) {
071                securityCache.put(command, hasAccess);
072            }
073        }
074
075        return hasAccess;
076    }
077
078    private <F extends BaseForm> boolean hasAccess(final GraphQlExecutionContext context,
079            final Class<? extends GraphQlSecurityCommand<F>> command, final F form) {
080        var graphQlSecurityCommand = CDI.current().select(command).get();
081
082        // Execute the instantiated command's security check function for the current user.
083        graphQlSecurityCommand.security(context.getUserVisitPK(), form);
084
085        // User will have access to this GraphQL field as long as there are no security
086        // messages (errors) generated by the command.
087        return !graphQlSecurityCommand.hasSecurityMessages();
088    }
089
090}