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.petstore.console.dev; 018 019import java.security.*; 020import java.util.*; 021 022import org.apache.juneau.commons.inject.*; 023import org.apache.juneau.http.*; 024import org.apache.juneau.http.response.*; 025import org.apache.juneau.petstore.auth.*; 026import org.apache.juneau.petstore.console.*; 027import org.apache.juneau.petstore.dto.*; 028import org.apache.juneau.rest.server.*; 029import org.apache.juneau.rest.server.auth.*; 030import org.apache.juneau.rest.server.guard.*; 031 032/** 033 * The bearer-token protected API behind the Secure page. 034 * 035 * <p> 036 * Every op is gated by a fail-closed {@link BearerTokenGuard} backed by {@link StubBearerTokenValidator}: no token 037 * or an unknown token is a {@code 401} with a {@code WWW-Authenticate: Bearer realm="petstore"} challenge. On 038 * success the resolved {@link Principal} reaches ops as an {@link Auth @Auth} parameter. 039 * 040 * <p> 041 * <b>Why a guard rather than a filter chain?</b> {@code AuthFilterChain} composes several mechanisms and is 042 * fail-open by design, so a downstream guard decides. With one mechanism, the op-level guard is the whole story. 043 * 044 * <h5 class='section'>Example:</h5> 045 * <p class='bjava'> 046 * <jc>// One bean gates every op on the resource.</jc> 047 * <ja>@Bean</ja> 048 * <jk>public</jk> RestGuardList guards(BeanStore <jv>bs</jv>) { 049 * <jk>return</jk> RestGuardList.<jsm>create</jsm>(<jv>bs</jv>) 050 * .append(BearerTokenGuard.<jsm>create</jsm>().realm(<js>"petstore"</js>).validator(<jk>new</jk> StubBearerTokenValidator()).build()) 051 * .build(); 052 * } 053 * 054 * <jc>// curl -H 'Authorization: Bearer petstore-user' http://localhost:10000/console/dev/secure/api/pets</jc> 055 * </p> 056 */ 057@Rest(path="/api", title="Secure API") 058@SuppressWarnings({ 059 "java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page. 060}) 061public class SecureApiRest extends PetstoreConsolePage { 062 063 private static final long serialVersionUID = 1L; 064 065 /** 066 * Gates every op on this resource with a bearer-token guard. 067 * 068 * @param bs The bean store. 069 * @return The guard list. 070 */ 071 @Bean 072 public RestGuardList guards(BeanStore bs) { 073 return RestGuardList.create(bs) 074 .append(BearerTokenGuard.create().realm("petstore").validator(new StubBearerTokenValidator()).build()) 075 .build(); 076 } 077 078 /** @return All pets. */ 079 @RestGet(path="/pets") 080 public Collection<Pet> pets() { 081 return store().getPets(); 082 } 083 084 /** 085 * @param id The pet id. 086 * @return The pet. 087 * @throws NotFound If there is no such pet. 088 */ 089 @RestGet(path="/pets/{id}") 090 public Pet pet(@Path("id") long id) { 091 var pet = store().getPet(id); 092 if (pet == null) 093 throw new NotFound("Unknown pet '%s'", id); 094 return pet; 095 } 096 097 /** 098 * @param caller The authenticated principal (never <jk>null</jk>: the guard rejects anonymous calls first). 099 * @return {@code {name: <principal>}}. 100 */ 101 @RestGet(path="/whoami") 102 public Map<String,String> whoami(@Auth Principal caller) { 103 return Map.of("name", caller.getName()); 104 } 105}