001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.openid.connect.sdk; 019 020 021import java.util.*; 022 023import net.jcip.annotations.NotThreadSafe; 024 025import com.nimbusds.oauth2.sdk.ParseException; 026import com.nimbusds.oauth2.sdk.util.StringUtils; 027 028 029/** 030 * Prompts for end-user re-authentication and consent. 031 * 032 * <p>Related specifications: 033 * 034 * <ul> 035 * <li>OpenID Connect Core 1.0, section 3.1.2.1. 036 * </ul> 037 */ 038@NotThreadSafe 039public class Prompt extends LinkedHashSet<Prompt.Type> { 040 041 042 /** 043 * Enumeration of the prompt types. 044 */ 045 public enum Type { 046 047 048 /** 049 * The authorisation server must not display any authentication 050 * or consent UI pages. An error is returned if the end user is 051 * not already authenticated or the client does not have 052 * pre-configured consent for the requested {@code scope}. This 053 * can be used as a method to check for existing authentication 054 * and / or consent. 055 */ 056 NONE, 057 058 059 /** 060 * The authorisation server must prompt the end-user for 061 * re-authentication. 062 */ 063 LOGIN, 064 065 066 /** 067 * The authorisation server must prompt the end-user for 068 * consent before returning information to the client. 069 */ 070 CONSENT, 071 072 073 /** 074 * The authorisation server must prompt the end-user to select 075 * a user account. This allows a user who has multiple accounts 076 * at the authorisation server to select amongst the multiple 077 * accounts that they may have current sessions for. 078 */ 079 SELECT_ACCOUNT; 080 081 082 /** 083 * Returns the string identifier of this prompt type. 084 * 085 * @return The string identifier. 086 */ 087 @Override 088 public String toString() { 089 090 return super.toString().toLowerCase(); 091 } 092 093 094 /** 095 * Parses a prompt type. 096 * 097 * @param s The string to parse. 098 * 099 * @return The prompt type. 100 * 101 * @throws ParseException If the parsed string is {@code null} 102 * or doesn't match a prompt type. 103 */ 104 public static Type parse(final String s) 105 throws ParseException { 106 107 if (StringUtils.isBlank(s)) 108 throw new ParseException("Null or empty prompt type string"); 109 110 if ("none".equals(s)) { 111 return NONE; 112 } else if ("login".equals(s)) { 113 return LOGIN; 114 } else if ("consent".equals(s)) { 115 return CONSENT; 116 } else if ("select_account".equals(s)) { 117 return SELECT_ACCOUNT; 118 } else { 119 throw new ParseException("Unknown prompt type: " + s); 120 } 121 } 122 } 123 124 125 /** 126 * Creates a new empty prompt. 127 */ 128 public Prompt() { 129 130 // Nothing to do 131 } 132 133 134 /** 135 * Creates a new prompt with the specified types. 136 * 137 * @param type The prompt types. 138 */ 139 public Prompt(final Type ... type) { 140 141 addAll(Arrays.asList(type)); 142 } 143 144 145 /** 146 * Creates a new prompt with the specified type values. 147 * 148 * @param values The prompt type values. 149 * 150 * @throws java.lang.IllegalArgumentException If the type value is 151 * invalid. 152 */ 153 public Prompt(final String ... values) { 154 155 for (String v: values) { 156 157 try { 158 add(Type.parse(v)); 159 160 } catch (ParseException e) { 161 162 throw new IllegalArgumentException(e.getMessage(), e); 163 } 164 } 165 } 166 167 168 /** 169 * Checks if the prompt is valid. This is done by examining the prompt 170 * for a conflicting {@link Type#NONE} value. 171 * 172 * @return {@code true} if this prompt if valid, else {@code false}. 173 */ 174 public boolean isValid() { 175 176 return !(size() > 1 && contains(Type.NONE)); 177 } 178 179 180 /** 181 * Returns the string list representation of this prompt. 182 * 183 * @return The string list representation. 184 */ 185 public List<String> toStringList() { 186 187 List<String> list = new ArrayList<>(this.size()); 188 189 for (Type t: this) 190 list.add(t.toString()); 191 192 return list; 193 } 194 195 196 /** 197 * Returns the string representation of this prompt. The values are 198 * delimited by space. 199 * 200 * <p>Example: 201 * 202 * <pre> 203 * login consent 204 * </pre> 205 * 206 * @return The string representation. 207 */ 208 @Override 209 public String toString() { 210 211 StringBuilder sb = new StringBuilder(); 212 213 Iterator<Type> it = super.iterator(); 214 215 while (it.hasNext()) { 216 217 sb.append(it.next().toString()); 218 219 if (it.hasNext()) 220 sb.append(" "); 221 } 222 223 return sb.toString(); 224 } 225 226 227 /** 228 * Parses a prompt from the specified string list. 229 * 230 * @param collection The string list to parse, with one or more 231 * non-conflicting prompt types. May be {@code null}. 232 * 233 * @return The prompt, {@code null} if the parsed string list was 234 * {@code null} or empty. 235 * 236 * @throws ParseException If the string list couldn't be parsed to a 237 * valid prompt. 238 */ 239 public static Prompt parse(final Collection<String> collection) 240 throws ParseException { 241 242 if (collection == null) 243 return null; 244 245 Prompt prompt = new Prompt(); 246 247 for (String s: collection) 248 prompt.add(Prompt.Type.parse(s)); 249 250 if (! prompt.isValid()) 251 throw new ParseException("Invalid prompt: " + collection); 252 253 return prompt; 254 } 255 256 257 /** 258 * Parses a prompt from the specified string. 259 * 260 * @param s The string to parse, with one or more non-conflicting space 261 * delimited prompt types. May be {@code null}. 262 * 263 * @return The prompt, {@code null} if the parsed string was 264 * {@code null} or empty. 265 * 266 * @throws ParseException If the string couldn't be parsed to a valid 267 * prompt. 268 */ 269 public static Prompt parse(final String s) 270 throws ParseException { 271 272 if (StringUtils.isBlank(s)) 273 return null; 274 275 Prompt prompt = new Prompt(); 276 277 StringTokenizer st = new StringTokenizer(s, " "); 278 279 while (st.hasMoreTokens()) 280 prompt.add(Prompt.Type.parse(st.nextToken())); 281 282 if (! prompt.isValid()) 283 throw new ParseException("Invalid prompt: " + s); 284 285 return prompt; 286 } 287}