001package com.nimbusds.oauth2.sdk; 002 003 004import java.util.ArrayList; 005import java.util.LinkedHashSet; 006import java.util.List; 007import java.util.StringTokenizer; 008 009import net.jcip.annotations.Immutable; 010import net.jcip.annotations.NotThreadSafe; 011 012import com.nimbusds.oauth2.sdk.id.Identifier; 013import java.util.Collection; 014 015 016/** 017 * Authorisation scope. This class is not thread-safe. 018 * 019 * <p>Example scope from OpenID Connect indicating access to the user's email 020 * and profile details: 021 * 022 * <pre> 023 * Scope scope = new Scope(); 024 * scope.add(OIDCScopeValue.OPENID); 025 * scope.add(OIDCScopeValue.EMAIL); 026 * scope.add(OIDCScopeValue.PROFILE); 027 * </pre> 028 * 029 * <p>Related specifications: 030 * 031 * <ul> 032 * <li>OAuth 2.0 (RFC 6749), section 3.3. 033 * </ul> 034 * 035 * @author Vladimir Dzhuvinov 036 */ 037@NotThreadSafe 038public class Scope extends LinkedHashSet<Scope.Value> { 039 040 041 /** 042 * Authorisation scope value. This class is immutable. 043 */ 044 @Immutable 045 public static class Value extends Identifier { 046 047 048 /** 049 * Enumeration of the scope value requirements for 050 * application-specific authorisation requests. 051 */ 052 public static enum Requirement { 053 054 055 /** 056 * The value must be present in the {@link Scope} 057 * parameter. 058 */ 059 REQUIRED, 060 061 062 /** 063 * The value may be optionally included in the 064 * {@link Scope} parameter. 065 */ 066 OPTIONAL 067 } 068 069 070 /** 071 * Optional requirement. 072 */ 073 private final Value.Requirement requirement; 074 075 076 /** 077 * Creates a new scope value. The requirement is not specified. 078 * 079 * @param value The scope value. Must not be {@code null} or 080 * empty string. 081 */ 082 public Value(final String value) { 083 084 this(value, null); 085 } 086 087 /** 088 * Creates a new scope value with an optional requirement. 089 * 090 * @param value The scope value. Must not be {@code null} 091 * or empty string. 092 * @param requirement The requirement, {@code null} if not 093 * specified. 094 */ 095 public Value(final String value, final Requirement requirement) { 096 097 super(value); 098 099 this.requirement = requirement; 100 } 101 102 103 /** 104 * Gets the requirement of this scope value. 105 * 106 * @return The requirement, {@code null} if not specified. 107 */ 108 public Requirement getRequirement() { 109 110 return requirement; 111 } 112 113 114 @Override 115 public boolean equals(final Object object) { 116 117 return object != null && 118 object instanceof Value && 119 this.toString().equals(object.toString()); 120 } 121 } 122 123 124 /** 125 * Creates a new empty authorisation scope. 126 */ 127 public Scope() { 128 // Nothing to do 129 } 130 131 132 /** 133 * Returns the string list representation of this scope. The scope 134 * values will be serialised in the order they were added. 135 * 136 * @return The string list representation. 137 */ 138 public List<String> toStringList() { 139 140 List<String> list = new ArrayList<String>(this.size()); 141 142 for (Scope.Value v: this) 143 list.add(v.getValue()); 144 145 return list; 146 } 147 148 149 /** 150 * Returns the string representation of this scope. The scope values 151 * will be serialised in the order they were added. 152 * 153 * @return The string representation. 154 */ 155 @Override 156 public String toString() { 157 158 StringBuilder sb = new StringBuilder(); 159 160 for (Scope.Value v : this) { 161 162 if (sb.length() > 0) { 163 sb.append(' '); 164 } 165 166 sb.append(v.toString()); 167 } 168 169 return sb.toString(); 170 } 171 172 173 /** 174 * Parses a scope from the specified string collection representation. 175 * 176 * @param collection The string collection, {@code null} if not 177 * specified. 178 * 179 * @return The scope, {@code null} if not specified. 180 */ 181 public static Scope parse(final Collection<String> collection) { 182 183 if (collection == null) 184 return null; 185 186 Scope scope = new Scope(); 187 188 for (String v: collection) 189 scope.add(new Scope.Value(v)); 190 191 return scope; 192 } 193 194 195 /** 196 * Parses a scope from the specified string representation. 197 * 198 * @param s The scope string, {@code null} if not specified. 199 * 200 * @return The scope, {@code null} if not specified. 201 */ 202 public static Scope parse(final String s) { 203 204 if (s == null) 205 return null; 206 207 Scope scope = new Scope(); 208 209 if (s.trim().isEmpty()) 210 return scope; 211 212 StringTokenizer st = new StringTokenizer(s, " "); 213 214 while(st.hasMoreTokens()) 215 scope.add(new Scope.Value(st.nextToken())); 216 217 return scope; 218 } 219}