FormConfiguration.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2017 The PWM Project
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. package password.pwm.config;
  23. import password.pwm.AppProperty;
  24. import password.pwm.PwmConstants;
  25. import password.pwm.error.ErrorInformation;
  26. import password.pwm.error.PwmDataValidationException;
  27. import password.pwm.error.PwmError;
  28. import password.pwm.error.PwmOperationalException;
  29. import password.pwm.error.PwmUnrecoverableException;
  30. import password.pwm.i18n.Display;
  31. import password.pwm.util.LocaleHelper;
  32. import password.pwm.util.java.JsonUtil;
  33. import password.pwm.util.java.StringUtil;
  34. import java.io.Serializable;
  35. import java.math.BigInteger;
  36. import java.util.ArrayList;
  37. import java.util.Collection;
  38. import java.util.Collections;
  39. import java.util.List;
  40. import java.util.Locale;
  41. import java.util.Map;
  42. import java.util.StringTokenizer;
  43. import java.util.regex.Matcher;
  44. import java.util.regex.Pattern;
  45. import java.util.regex.PatternSyntaxException;
  46. /**
  47. * @author Jason D. Rivard
  48. */
  49. public class FormConfiguration implements Serializable {
  50. // ------------------------------ FIELDS ------------------------------
  51. public enum Type {text, email, number, password, random, tel, hidden, date, datetime, time, week, month, url, select, userDN, checkbox}
  52. private String name;
  53. private int minimumLength;
  54. private int maximumLength;
  55. private Type type = Type.text;
  56. private boolean required;
  57. private boolean confirmationRequired;
  58. private boolean readonly;
  59. private boolean unique;
  60. private boolean multivalue;
  61. private Map<String,String> labels = Collections.singletonMap("", "");
  62. private Map<String,String> regexErrors = Collections.singletonMap("","");
  63. private Map<String,String> description = Collections.singletonMap("","");
  64. private String regex;
  65. private String placeholder;
  66. private String javascript;
  67. private Map<String,String> selectOptions = Collections.emptyMap();
  68. // -------------------------- STATIC METHODS --------------------------
  69. public static FormConfiguration parseOldConfigString(final String config)
  70. throws PwmOperationalException
  71. {
  72. if (config == null) {
  73. throw new NullPointerException("config can not be null");
  74. }
  75. final FormConfiguration formItem = new FormConfiguration();
  76. final StringTokenizer st = new StringTokenizer(config, ":");
  77. // attribute name
  78. formItem.name = st.nextToken();
  79. // label
  80. formItem.labels = Collections.singletonMap("",st.nextToken());
  81. // type
  82. {
  83. final String typeStr = st.nextToken();
  84. try {
  85. formItem.type = Type.valueOf(typeStr.toLowerCase());
  86. } catch (IllegalArgumentException e) {
  87. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"unknown type for form config: " + typeStr}));
  88. }
  89. }
  90. //minimum length
  91. try {
  92. formItem.minimumLength = Integer.parseInt(st.nextToken());
  93. } catch (NumberFormatException e) {
  94. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"invalid minimum length type for form config: " + e.getMessage()}));
  95. }
  96. //maximum length
  97. try {
  98. formItem.maximumLength = Integer.parseInt(st.nextToken());
  99. } catch (NumberFormatException e) {
  100. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"invalid maximum length type for form config: " + e.getMessage()}));
  101. }
  102. //required
  103. formItem.required = Boolean.TRUE.toString().equalsIgnoreCase(st.nextToken());
  104. //confirmation
  105. formItem.confirmationRequired = Boolean.TRUE.toString().equalsIgnoreCase(st.nextToken());
  106. return formItem;
  107. }
  108. public void validate() throws PwmOperationalException {
  109. if (this.getName() == null || this.getName().length() < 1) {
  110. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" form field name is required"}));
  111. }
  112. if (this.getType() == null) {
  113. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" type is required for field " + this.getName()}));
  114. }
  115. if (labels == null || this.labels.isEmpty() || this.getLabel(PwmConstants.DEFAULT_LOCALE) == null || this.getLabel(PwmConstants.DEFAULT_LOCALE).length() < 1) {
  116. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" a default label value is required for " + this.getName()}));
  117. }
  118. if (this.getRegex() != null && this.getRegex().length() > 0) {
  119. try {
  120. Pattern.compile(this.getRegex());
  121. } catch (PatternSyntaxException e) {
  122. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" regular expression for '" + this.getName() + " ' is not valid: " + e.getMessage()}));
  123. }
  124. }
  125. if (this.getType() == Type.select) {
  126. if (this.getSelectOptions() == null || this.getSelectOptions().isEmpty()) {
  127. throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" field '" + this.getName() + " ' is type select, but no select options are defined"}));
  128. }
  129. }
  130. }
  131. // --------------------------- CONSTRUCTORS ---------------------------
  132. public FormConfiguration() {
  133. labels = Collections.singletonMap("","");
  134. regexErrors = Collections.singletonMap("","");
  135. }
  136. // --------------------- GETTER / SETTER METHODS ---------------------
  137. public String getName() {
  138. return name;
  139. }
  140. public String getLabel(final Locale locale) {
  141. return LocaleHelper.resolveStringKeyLocaleMap(locale, labels);
  142. }
  143. public Map<String,String> getLabelLocaleMap() {
  144. return Collections.unmodifiableMap(this.labels);
  145. }
  146. public String getRegexError(final Locale locale) {
  147. return LocaleHelper.resolveStringKeyLocaleMap(locale, regexErrors);
  148. }
  149. public String getDescription(final Locale locale) {
  150. return LocaleHelper.resolveStringKeyLocaleMap(locale, description);
  151. }
  152. public Map<String,String> getLabelDescriptionLocaleMap() {
  153. return Collections.unmodifiableMap(this.description);
  154. }
  155. public int getMaximumLength() {
  156. return maximumLength;
  157. }
  158. public int getMinimumLength() {
  159. return minimumLength;
  160. }
  161. public Type getType() {
  162. return type;
  163. }
  164. public boolean isConfirmationRequired() {
  165. return confirmationRequired;
  166. }
  167. public boolean isRequired() {
  168. return required;
  169. }
  170. public boolean isReadonly() {
  171. return readonly;
  172. }
  173. public boolean isUnique() {
  174. return unique;
  175. }
  176. public boolean isMultivalue() {
  177. return multivalue;
  178. }
  179. public String getRegex() {
  180. return regex;
  181. }
  182. public String getPlaceholder() {
  183. return placeholder;
  184. }
  185. public String getJavascript() {
  186. return javascript;
  187. }
  188. public Map<String,String> getSelectOptions() {
  189. return Collections.unmodifiableMap(selectOptions);
  190. }
  191. // ------------------------ CANONICAL METHODS ------------------------
  192. public boolean equals(final Object o) {
  193. if (this == o) {
  194. return true;
  195. }
  196. if (!(o instanceof FormConfiguration)) {
  197. return false;
  198. }
  199. final FormConfiguration parameterConfig = (FormConfiguration) o;
  200. return !(name != null ? !name.equals(parameterConfig.name) : parameterConfig.name != null);
  201. }
  202. public int hashCode() {
  203. return (name != null ? name.hashCode() : 0);
  204. }
  205. public String toString() {
  206. final StringBuilder sb = new StringBuilder();
  207. sb.append("FormItem: ");
  208. sb.append(JsonUtil.serialize(this));
  209. return sb.toString();
  210. }
  211. // -------------------------- OTHER METHODS --------------------------
  212. public void checkValue(final Configuration config, final String value, final Locale locale)
  213. throws PwmDataValidationException, PwmUnrecoverableException {
  214. //check if value is missing and required.
  215. if (required && (value == null || value.length() < 1)) {
  216. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_REQUIRED, null, new String[]{getLabel(locale)});
  217. throw new PwmDataValidationException(error);
  218. }
  219. switch (type) {
  220. case number:
  221. if (value != null && value.length() > 0) {
  222. try {
  223. new BigInteger(value);
  224. } catch (NumberFormatException e) {
  225. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_NOT_A_NUMBER, null, new String[]{getLabel(locale)});
  226. throw new PwmDataValidationException(error);
  227. }
  228. }
  229. break;
  230. case email:
  231. if (value != null && value.length() > 0) {
  232. if (!testEmailAddress(config, value)) {
  233. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_INVALID_EMAIL, null, new String[]{getLabel(locale)});
  234. throw new PwmDataValidationException(error);
  235. }
  236. }
  237. break;
  238. default:
  239. // continue for other types
  240. break;
  241. }
  242. if (value != null && (this.getMinimumLength() > 0) && (value.length() > 0) && (value.length() < this.getMinimumLength())) {
  243. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_TOO_SHORT, null, new String[]{getLabel(locale)});
  244. throw new PwmDataValidationException(error);
  245. }
  246. if (value != null && value.length() > this.getMaximumLength()) {
  247. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_TOO_LONG, null, new String[]{getLabel(locale)});
  248. throw new PwmDataValidationException(error);
  249. }
  250. if (value != null && value.length() > 0 && this.getRegex() != null && this.getRegex().length() > 0) {
  251. if (!value.matches(this.getRegex())) {
  252. final String configuredErrorMessage = this.getRegexError(locale);
  253. final ErrorInformation error = new ErrorInformation(PwmError.ERROR_FIELD_REGEX_NOMATCH, null, configuredErrorMessage, new String[]{getLabel(locale)});
  254. throw new PwmDataValidationException(error);
  255. }
  256. }
  257. }
  258. public static List<String> convertToListOfNames(final Collection<FormConfiguration> formConfigurations) {
  259. if (formConfigurations == null) {
  260. return Collections.emptyList();
  261. }
  262. final ArrayList<String> returnList = new ArrayList<>();
  263. for (final FormConfiguration formConfiguration : formConfigurations) {
  264. returnList.add(formConfiguration.getName());
  265. }
  266. return returnList;
  267. }
  268. /**
  269. * Return false if an invalid email address is issued.
  270. * @param config
  271. * @param address
  272. * @return
  273. */
  274. public static boolean testEmailAddress(final Configuration config, final String address) {
  275. final String patternStr;
  276. if (config != null) {
  277. patternStr = config.readAppProperty(AppProperty.FORM_EMAIL_REGEX);
  278. } else {
  279. patternStr = AppProperty.FORM_EMAIL_REGEX.getDefaultValue();
  280. }
  281. final Pattern pattern = Pattern.compile(patternStr);
  282. final Matcher matcher = pattern.matcher(address);
  283. return matcher.matches();
  284. }
  285. public String displayValue(final String value, final Locale locale, final Configuration config) {
  286. if (value == null) {
  287. return LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, config);
  288. }
  289. if (this.getType() == Type.select) {
  290. if (this.getSelectOptions() != null) {
  291. for (final String key : selectOptions.keySet()) {
  292. if (value.equals(key)) {
  293. final String displayValue = selectOptions.get(key);
  294. if (!StringUtil.isEmpty(displayValue)) {
  295. return displayValue;
  296. }
  297. }
  298. }
  299. }
  300. }
  301. return value;
  302. }
  303. }