001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.console.views;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022
023/**
024 * Serving-path allowlist of populator names that may be named on a region ({@link RegionDef#populate}).
025 *
026 * <p>
027 * This is an <b>authoring-discipline control, not a security boundary</b>, and the reason is structural, not a
028 * weaker policy choice.  {@link SinkRenderAllowlist} is real defense: a named renderer's markup still has to pass
029 * through the closed {@code fillRenderSlot} copier, so an accepted id only ever gets to write what that copier lets
030 * it write. A populator has no such copier anywhere in its path &mdash; {@code JuneauViews.regions.resolve(name)}
031 * hands the resolved function the region's container element outright, and from that point the populator owns the
032 * container completely. This class therefore controls only <i>which name is resolved</i>; it says nothing whatever
033 * about what the resolved populator then does with the container it is handed. A reader who treats this allowlist
034 * as a security boundary equivalent to {@link SinkRenderAllowlist} will over-trust it.
035 *
036 * @since 10.0.0
037 */
038public final class PopulatorAllowlist {
039
040   /**
041    * The reserved declarative-default populator name &mdash; the only Juneau-shipped populator today.
042    *
043    * <p>
044    * Unlike an ordinary {@code JuneauViews.regions.register(name, fn)} entry, which is last-write-wins and freely
045    * overridable by a later registration under the same name, the client registry refuses to let a consumer
046    * override the entry named <js>"default"</js>: that name is reserved for the framework's own declarative-default
047    * populator.
048    */
049   public static final Set<String> BUILTIN_IDS = Set.of("default");
050
051   private PopulatorAllowlist() {}
052
053   /**
054    * Accepts a reserved built-in name, or a name present in {@code allowedNames}.
055    *
056    * @param populatorName The populator name.  Must not be <jk>null</jk> or blank.
057    * @param allowedNames Opt-in populator names (e.g. {@link RegionDef#allowedPopulators}).  Can be <jk>null</jk>.
058    * @throws IllegalArgumentException If the name is neither a built-in nor an opted-in name.
059    */
060   public static void assertAllowed(String populatorName, Collection<String> allowedNames) {
061      if (ib(populatorName))
062         throw iaex("Populator name must not be null or blank.");
063      if (BUILTIN_IDS.contains(populatorName))
064         return;
065      if (allowedNames != null && allowedNames.contains(populatorName))
066         return;
067      throw iaex("Populator name '%s' is not an allowed region populator.", populatorName);
068   }
069}