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 — 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 — 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 — that is out of scope for a UI-rendering toolkit — 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 — it has no 077 * journal, arming, or idempotency logic of its own to check it against — 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}