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 ForgeRock AS. 015 */ 016 017package org.forgerock.openig.filter.oauth2.challenge; 018 019import static java.lang.String.*; 020 021import java.util.Collections; 022import java.util.Set; 023 024import org.forgerock.openig.http.Response; 025 026/** 027 * Builds an error {@link Response} when the request is missing some required scope. 028 * <p> 029 * Example: 030 * <pre> 031 * HTTP/1.1 403 Forbidden 032 * WWW-Authenticate: Bearer realm="example", 033 * error="insufficient_scope", 034 * error_description="....", 035 * scope="openid profile email" 036 * </pre> 037 */ 038public class InsufficientScopeChallengeHandler extends AuthenticateChallengeHandler { 039 040 private static final String INSUFFICIENT_SCOPE_DESCRIPTION = "The request requires higher privileges than " 041 + "provided by the access token."; 042 043 private final Set<String> scopes; 044 045 /** 046 * Builds a new InsufficientScopeChallengeHandler with a default description, no error URI page and no scopes. 047 * 048 * @param realm 049 * mandatory realm value. 050 */ 051 public InsufficientScopeChallengeHandler(final String realm) { 052 this(realm, Collections.<String>emptySet()); 053 } 054 055 /** 056 * Builds a new InsufficientScopeChallengeHandler with a default description and no error URI page. 057 * 058 * @param realm 059 * mandatory realm value. 060 * @param scopes 061 * List of required scopes (will be omitted if empty) 062 */ 063 public InsufficientScopeChallengeHandler(final String realm, final Set<String> scopes) { 064 this(realm, scopes, null); 065 } 066 067 /** 068 * Builds a new InsufficientScopeChallengeHandler with a default description. 069 * 070 * @param realm 071 * mandatory realm value. 072 * @param scopes 073 * List of required scopes (will be omitted if empty) 074 * @param insufficientScopeUri 075 * error uri page (will be omitted if {@literal null}) 076 */ 077 public InsufficientScopeChallengeHandler(final String realm, 078 final Set<String> scopes, 079 final String insufficientScopeUri) { 080 this(realm, INSUFFICIENT_SCOPE_DESCRIPTION, scopes, insufficientScopeUri); 081 } 082 083 /** 084 * Builds a new InsufficientScopeChallengeHandler. 085 * 086 * @param realm 087 * mandatory realm value. 088 * @param description 089 * error description (will be omitted if {@literal null}) 090 * @param scopes 091 * List of required scopes (will be omitted if empty) 092 * @param insufficientScopeUri 093 * error uri page (will be omitted if {@literal null}) 094 */ 095 public InsufficientScopeChallengeHandler(final String realm, 096 final String description, 097 final Set<String> scopes, 098 final String insufficientScopeUri) { 099 super(realm, "insufficient_scope", description, insufficientScopeUri); 100 this.scopes = scopes; 101 } 102 103 @Override 104 protected Response createResponse() { 105 Response response = new Response(); 106 response.setStatus(403); 107 response.setReason("Forbidden"); 108 return response; 109 } 110 111 @Override 112 protected void appendExtraAttributes(final StringBuilder sb) { 113 StringBuilder scopeAttr = buildSpaceSeparatedScopeList(); 114 if (scopeAttr.length() != 0) { 115 sb.append(format(", scope=\"%s\"", scopeAttr.toString())); 116 } 117 } 118 119 private StringBuilder buildSpaceSeparatedScopeList() { 120 StringBuilder scopeAttr = new StringBuilder(); 121 for (String scope : scopes) { 122 if (scopeAttr.length() != 0) { 123 scopeAttr.append(" "); 124 } 125 scopeAttr.append(scope); 126 } 127 return scopeAttr; 128 } 129 130 131}