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 instanceof Value && 118 this.toString().equals(object.toString()); 119 } 120 } 121 122 123 /** 124 * Creates a new empty authorisation scope. 125 */ 126 public Scope() { 127 // Nothing to do 128 } 129 130 131 /** 132 * Returns the string list representation of this scope. The scope 133 * values will be serialised in the order they were added. 134 * 135 * @return The string list representation. 136 */ 137 public List<String> toStringList() { 138 139 List<String> list = new ArrayList<String>(this.size()); 140 141 for (Scope.Value v: this) 142 list.add(v.getValue()); 143 144 return list; 145 } 146 147 148 /** 149 * Returns the string representation of this scope. The scope values 150 * will be serialised in the order they were added. 151 * 152 * @return The string representation. 153 */ 154 @Override 155 public String toString() { 156 157 StringBuilder sb = new StringBuilder(); 158 159 for (Scope.Value v : this) { 160 161 if (sb.length() > 0) { 162 sb.append(' '); 163 } 164 165 sb.append(v.toString()); 166 } 167 168 return sb.toString(); 169 } 170 171 172 /** 173 * Parses a scope from the specified string collection representation. 174 * 175 * @param collection The string collection, {@code null} if not 176 * specified. 177 * 178 * @return The scope, {@code null} if not specified. 179 */ 180 public static Scope parse(final Collection<String> collection) { 181 182 if (collection == null) 183 return null; 184 185 Scope scope = new Scope(); 186 187 for (String v: collection) 188 scope.add(new Scope.Value(v)); 189 190 return scope; 191 } 192 193 194 /** 195 * Parses a scope from the specified string representation. 196 * 197 * @param s The scope string, {@code null} if not specified. 198 * 199 * @return The scope, {@code null} if not specified. 200 */ 201 public static Scope parse(final String s) { 202 203 if (s == null) 204 return null; 205 206 Scope scope = new Scope(); 207 208 if (s.trim().isEmpty()) 209 return scope; 210 211 StringTokenizer st = new StringTokenizer(s, " "); 212 213 while(st.hasMoreTokens()) 214 scope.add(new Scope.Value(st.nextToken())); 215 216 return scope; 217 } 218}