001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.openig.filter.oauth2.challenge; 018 019import static java.lang.String.format; 020 021import java.util.Collections; 022import java.util.Set; 023 024import org.forgerock.http.protocol.Response; 025import org.forgerock.http.protocol.Status; 026 027/** 028 * Builds an error {@link Response} when the request is missing some required scope. 029 * <p> 030 * Example: 031 * <pre> 032 * {@code 033 * HTTP/1.1 403 Forbidden 034 * WWW-Authenticate: Bearer realm="example", 035 * error="insufficient_scope", 036 * error_description="....", 037 * scope="openid profile email" 038 * } 039 * </pre> 040 */ 041public class InsufficientScopeChallengeHandler extends AuthenticateChallengeHandler { 042 043 private static final String INSUFFICIENT_SCOPE_DESCRIPTION = "The request requires higher privileges than " 044 + "provided by the access token."; 045 046 private final Set<String> scopes; 047 048 /** 049 * Builds a new InsufficientScopeChallengeHandler with a default description, no error URI page and no scopes. 050 * 051 * @param realm 052 * mandatory realm value. 053 */ 054 public InsufficientScopeChallengeHandler(final String realm) { 055 this(realm, Collections.<String>emptySet()); 056 } 057 058 /** 059 * Builds a new InsufficientScopeChallengeHandler with a default description and no error URI page. 060 * 061 * @param realm 062 * mandatory realm value. 063 * @param scopes 064 * List of required scopes (will be omitted if empty) 065 */ 066 public InsufficientScopeChallengeHandler(final String realm, final Set<String> scopes) { 067 this(realm, scopes, null); 068 } 069 070 /** 071 * Builds a new InsufficientScopeChallengeHandler with a default description. 072 * 073 * @param realm 074 * mandatory realm value. 075 * @param scopes 076 * List of required scopes (will be omitted if empty) 077 * @param insufficientScopeUri 078 * error uri page (will be omitted if {@literal null}) 079 */ 080 public InsufficientScopeChallengeHandler(final String realm, 081 final Set<String> scopes, 082 final String insufficientScopeUri) { 083 this(realm, INSUFFICIENT_SCOPE_DESCRIPTION, scopes, insufficientScopeUri); 084 } 085 086 /** 087 * Builds a new InsufficientScopeChallengeHandler. 088 * 089 * @param realm 090 * mandatory realm value. 091 * @param description 092 * error description (will be omitted if {@literal null}) 093 * @param scopes 094 * List of required scopes (will be omitted if empty) 095 * @param insufficientScopeUri 096 * error uri page (will be omitted if {@literal null}) 097 */ 098 public InsufficientScopeChallengeHandler(final String realm, 099 final String description, 100 final Set<String> scopes, 101 final String insufficientScopeUri) { 102 super(realm, "insufficient_scope", description, insufficientScopeUri); 103 this.scopes = scopes; 104 } 105 106 @Override 107 protected Response createResponse() { 108 Response response = new Response(); 109 response.setStatus(Status.FORBIDDEN); 110 return response; 111 } 112 113 @Override 114 protected void appendExtraAttributes(final StringBuilder sb) { 115 StringBuilder scopeAttr = buildSpaceSeparatedScopeList(); 116 if (scopeAttr.length() != 0) { 117 sb.append(format(", scope=\"%s\"", scopeAttr.toString())); 118 } 119 } 120 121 private StringBuilder buildSpaceSeparatedScopeList() { 122 StringBuilder scopeAttr = new StringBuilder(); 123 for (String scope : scopes) { 124 if (scopeAttr.length() != 0) { 125 scopeAttr.append(" "); 126 } 127 scopeAttr.append(scope); 128 } 129 return scopeAttr; 130 } 131 132 133}