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
021/**
022 * The views-toolkit's own structural gate token for {@link BulkMutateDef}: proof that the caller's own write-gate
023 * was consulted before a bulk-mutating affordance is configured (the opt-in separability
024 * guarantee).
025 *
026 * <h5 class='section'>Why this exists, and why it is NOT the console's own {@code WritePermit}</h5>
027 * <p>
028 * A consuming application (e.g. the Sandbox Support Console) may already have its own, much richer write-gate
029 * token &mdash; one bound to a journal, a box {@code Mode}, an idempotency key, and a target validator (see that
030 * application's own {@code write.WritePermit}/{@code WriteGuard}). That type is deliberately <b>not</b> reused
031 * here: its constructor is package-private to its own {@code write} package, it is defined in a console-ui
032 * artifact this framework module must not depend on, and its shape carries concerns (journal binding, SAFE/LIVE
033 * mode) that are that application's business, not this framework's. Reaching into it would be a layering
034 * violation in the wrong direction (framework depending on a specific consumer).
035 *
036 * <p>
037 * This type is therefore this module's own, minimal, independent token &mdash; the same <i>discipline</i>
038 * (a required-parameter capability proof), a deliberately smaller <i>shape</i> (no journal/mode/idempotency
039 * concerns, which belong to the consumer's own gate). A consuming application's own write-gate is expected to
040 * mint one (typically by wrapping/adapting its own gate result) only after ITS OWN checks have passed, then hand
041 * it to {@link BulkMutateDef#create(WritePermit, SelectionDef)}. This module does not, and cannot, verify that the
042 * caller's gate logic is correct &mdash; that is out of scope for a UI-rendering toolkit &mdash; but it does
043 * guarantee, by construction, that {@link BulkMutateDef} cannot be reached at all without a permit in hand: there
044 * is no zero-argument overload, so omitting one is a <b>compile</b> failure, not a missed javadoc sentence
045 * (mirroring the discipline of the console's own {@code WriteGuard}).
046 *
047 * <h5 class='section'>Example:</h5>
048 * <p class='bjava'>
049 *    <jc>// After the consumer's OWN write-gate has already approved this capability:</jc>
050 *    WritePermit <jv>permit</jv> = WritePermit.<jsm>forCapability</jsm>(<js>"incident:bulk-ack"</js>);
051 *    BulkMutateDef <jv>bulk</jv> = BulkMutateDef.<jsm>create</jsm>(<jv>permit</jv>, <jv>selection</jv>)
052 *       .actions(RowAction.<jsm>create</jsm>(<js>"ack"</js>).endpoint(<js>"servlet:/incidents/{id}/ack"</js>).method(RowAction.Method.<jsf>POST</jsf>));
053 * </p>
054 *
055 * <h5 class='section'>See Also:</h5>
056 * <ul>
057 *    <li class='jc'>{@link BulkMutateDef}
058 *    <li class='jc'>{@link SelectionDef}
059 * </ul>
060 *
061 * @since 10.0.0
062 */
063public final class WritePermit {
064
065   private final String capability;
066
067   private WritePermit(String capability) {
068      this.capability = capability;
069   }
070
071   /**
072    * Mints a permit for the named capability.
073    *
074    * <p>
075    * This is a bare proof-of-authorization token, not an enforcement seam: minting one asserts that the CALLER's
076    * own write-gate has already approved {@code capability}. This module trusts that assertion &mdash; it has no
077    * journal, arming, or idempotency logic of its own to check it against &mdash; its only job is to make the
078    * bulk-mutate opt-in unreachable without one.
079    *
080    * @param capability A stable, human-readable identifier for the capability this permit authorizes (e.g.
081    *    {@code "incident:bulk-ack"}). Must not be <jk>null</jk> or blank.
082    * @return A new {@link WritePermit}.
083    * @throws IllegalArgumentException If {@code capability} is <jk>null</jk> or blank.
084    */
085   public static WritePermit forCapability(String capability) {
086      if (ib(capability))
087         throw iaex("WritePermit capability must not be null or blank.");
088      return new WritePermit(capability);
089   }
090
091   /**
092    * The capability this permit authorizes.
093    *
094    * @return The capability identifier passed to {@link #forCapability(String)}.
095    */
096   public String capability() {
097      return capability;
098   }
099
100   @Override /* Object */
101   public String toString() {
102      return "WritePermit(capability=" + capability + ")";
103   }
104}