PwmSettingMetaDataReader.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2021 The PWM Project
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package password.pwm.config;
  21. import org.jrivard.xmlchai.XmlElement;
  22. import password.pwm.PwmConstants;
  23. import password.pwm.config.value.PasswordValue;
  24. import password.pwm.config.value.StoredValue;
  25. import password.pwm.config.value.ValueFactory;
  26. import password.pwm.i18n.Config;
  27. import password.pwm.util.i18n.LocaleHelper;
  28. import password.pwm.util.java.JavaHelper;
  29. import password.pwm.util.java.LazySupplier;
  30. import password.pwm.util.java.StringUtil;
  31. import password.pwm.util.java.TimeDuration;
  32. import password.pwm.util.logging.PwmLogger;
  33. import password.pwm.util.macro.MacroRequest;
  34. import java.time.Instant;
  35. import java.util.ArrayList;
  36. import java.util.Collection;
  37. import java.util.Collections;
  38. import java.util.EnumMap;
  39. import java.util.EnumSet;
  40. import java.util.LinkedHashMap;
  41. import java.util.List;
  42. import java.util.Locale;
  43. import java.util.Map;
  44. import java.util.Optional;
  45. import java.util.Set;
  46. import java.util.function.Supplier;
  47. import java.util.regex.Pattern;
  48. import java.util.regex.PatternSyntaxException;
  49. public class PwmSettingMetaDataReader
  50. {
  51. private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSettingMetaDataReader.class );
  52. private final PwmSetting pwmSetting;
  53. // cached values read from XML file
  54. private final Supplier<List<PwmSetting.TemplateSetReference<StoredValue>>> defaultValues = new LazySupplier<>( () -> InternalReader.readDefaultValue( getPwmSetting() ) );
  55. private final Supplier<List<PwmSetting.TemplateSetReference<String>>> examples = new LazySupplier<>( () -> InternalReader.readExamples( getPwmSetting() ) );
  56. private final Supplier<Map<String, String>> options = new LazySupplier<>( () -> InternalReader.readOptions( getPwmSetting() ) );
  57. private final Supplier<Set<PwmSettingFlag>> flags = new LazySupplier<>( () -> InternalReader.readFlags( getPwmSetting() ) );
  58. private final Supplier<Map<PwmSettingProperty, String>> properties = new LazySupplier<>( () -> InternalReader.readProperties( getPwmSetting() ) );
  59. private final Supplier<Collection<LDAPPermissionInfo>> ldapPermissionInfo = new LazySupplier<>( () -> InternalReader.readLdapPermissionInfo( getPwmSetting() ) );
  60. private final Supplier<Boolean> required = new LazySupplier<>( () -> InternalReader.readRequired( getPwmSetting() ) );
  61. private final Supplier<Boolean> hidden = new LazySupplier<>( () -> InternalReader.readHidden( getPwmSetting() ) );
  62. private final Supplier<Integer> level = new LazySupplier<>( () -> InternalReader.readLevel( getPwmSetting() ) );
  63. private final Supplier<Pattern> pattern = new LazySupplier<>( () -> InternalReader.readPattern( getPwmSetting() ) );
  64. private final Supplier<String> defaultLocaleLabel = new LazySupplier<>( () -> InternalReader.readLabel( getPwmSetting(), PwmConstants.DEFAULT_LOCALE ) );
  65. private final Supplier<String> defaultLocaleDescription = new LazySupplier<>( () -> InternalReader.readDescription( getPwmSetting(), PwmConstants.DEFAULT_LOCALE ) );
  66. private final Supplier<String> defaultMenuLocation = new LazySupplier<>( () -> InternalReader.readMenuLocationDebugDefault( getPwmSetting() ) );
  67. public PwmSettingMetaDataReader( final PwmSetting pwmSetting )
  68. {
  69. this.pwmSetting = pwmSetting;
  70. }
  71. public PwmSetting getPwmSetting()
  72. {
  73. return pwmSetting;
  74. }
  75. public Map<PwmSettingProperty, String> getProperties( )
  76. {
  77. return properties.get();
  78. }
  79. public Set<PwmSettingFlag> getFlags( )
  80. {
  81. return flags.get();
  82. }
  83. public Map<String, String> getOptions()
  84. {
  85. return options.get();
  86. }
  87. public String getLabel( final Locale locale )
  88. {
  89. if ( PwmConstants.DEFAULT_LOCALE.equals( locale ) )
  90. {
  91. return defaultLocaleLabel.get();
  92. }
  93. return InternalReader.readLabel( pwmSetting, locale );
  94. }
  95. public String getDescription( final Locale locale )
  96. {
  97. if ( PwmConstants.DEFAULT_LOCALE.equals( locale ) )
  98. {
  99. return defaultLocaleDescription.get();
  100. }
  101. return InternalReader.readDescription( pwmSetting, locale );
  102. }
  103. public String getExample( final PwmSettingTemplateSet template )
  104. {
  105. return PwmSetting.TemplateSetReference.referenceForTempleSet( examples.get(), template );
  106. }
  107. public boolean isRequired( )
  108. {
  109. return required.get();
  110. }
  111. public boolean isHidden( )
  112. {
  113. return hidden.get();
  114. }
  115. public int getLevel( )
  116. {
  117. return level.get();
  118. }
  119. public Pattern getRegExPattern( )
  120. {
  121. return pattern.get();
  122. }
  123. public Collection<LDAPPermissionInfo> getLDAPPermissionInfo()
  124. {
  125. return ldapPermissionInfo.get();
  126. }
  127. List<PwmSetting.TemplateSetReference<StoredValue>> getDefaultValue()
  128. {
  129. return defaultValues.get();
  130. }
  131. public String toMenuLocationDebug(
  132. final String profileID,
  133. final Locale locale
  134. )
  135. {
  136. if ( PwmConstants.DEFAULT_LOCALE.equals( locale ) && StringUtil.isEmpty( profileID ) )
  137. {
  138. return defaultMenuLocation.get();
  139. }
  140. else
  141. {
  142. return InternalReader.readMenuLocationDebug( pwmSetting, profileID, locale );
  143. }
  144. }
  145. /**
  146. * Not required for normal operation, but executing this gets all the enum values populated form XML source. If run prior to users accessing the settings
  147. * module (particularly the config editor) it will increase the initial load performance significantly. There are no side effects to calling this operation
  148. * other than cache population.
  149. */
  150. public static void initCache()
  151. {
  152. final Instant startTime = Instant.now();
  153. for ( final PwmSetting pwmSetting : EnumSet.allOf( PwmSetting.class ) )
  154. {
  155. pwmSetting.getProperties();
  156. pwmSetting.getFlags();
  157. pwmSetting.getOptions();
  158. pwmSetting.getLabel( PwmConstants.DEFAULT_LOCALE );
  159. pwmSetting.getDescription( PwmConstants.DEFAULT_LOCALE );
  160. pwmSetting.getExample( PwmSettingTemplateSet.getDefault() );
  161. pwmSetting.isRequired();
  162. pwmSetting.isHidden();
  163. pwmSetting.getLevel();
  164. pwmSetting.getRegExPattern();
  165. pwmSetting.getLDAPPermissionInfo();
  166. pwmSetting.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE );
  167. }
  168. for ( final PwmSettingCategory pwmSettingCategory : EnumSet.allOf( PwmSettingCategory.class ) )
  169. {
  170. pwmSettingCategory.getLabel( PwmConstants.DEFAULT_LOCALE );
  171. pwmSettingCategory.getDescription( PwmConstants.DEFAULT_LOCALE );
  172. pwmSettingCategory.isHidden();
  173. pwmSettingCategory.getLevel();
  174. pwmSettingCategory.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE );
  175. pwmSettingCategory.getScope();
  176. pwmSettingCategory.getChildren();
  177. pwmSettingCategory.getLevel();
  178. pwmSettingCategory.getSettings();
  179. }
  180. LOGGER.trace( () -> "completed PwmSetting xml cache initialization", () -> TimeDuration.fromCurrent( startTime ) );
  181. }
  182. private static class InternalReader
  183. {
  184. private static Set<PwmSettingFlag> readFlags( final PwmSetting pwmSetting )
  185. {
  186. final Set<PwmSettingFlag> returnObj = EnumSet.noneOf( PwmSettingFlag.class );
  187. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  188. final List<XmlElement> flagElements = settingElement.getChildren( "flag" );
  189. for ( final XmlElement flagElement : flagElements )
  190. {
  191. final String value = flagElement.getText().orElse( "" ).trim();
  192. try
  193. {
  194. final PwmSettingFlag flag = PwmSettingFlag.valueOf( value );
  195. returnObj.add( flag );
  196. }
  197. catch ( final IllegalArgumentException e )
  198. {
  199. LOGGER.error( () -> "unknown flag for setting " + pwmSetting.getKey() + ", error: unknown flag value: " + value );
  200. }
  201. }
  202. return Collections.unmodifiableSet( returnObj );
  203. }
  204. private static Map<String, String> readOptions( final PwmSetting pwmSetting )
  205. {
  206. final Map<String, String> returnData = new LinkedHashMap<>();
  207. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  208. final Optional<XmlElement> optionsElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_OPTIONS );
  209. if ( optionsElement.isPresent() )
  210. {
  211. final List<XmlElement> optionElements = optionsElement.get().getChildren( PwmSettingXml.XML_ELEMENT_OPTION );
  212. if ( optionElements != null )
  213. {
  214. for ( final XmlElement optionElement : optionElements )
  215. {
  216. final String value = optionElement.getAttribute( PwmSettingXml.XML_ELEMENT_VALUE )
  217. .orElseThrow( () -> new IllegalStateException( "option element is missing 'value' attribute for key " + pwmSetting.getKey() ) );
  218. optionElement.getText().ifPresent( textValue -> returnData.put( value, textValue ) );
  219. }
  220. }
  221. }
  222. return Collections.unmodifiableMap( returnData );
  223. }
  224. private static Collection<LDAPPermissionInfo> readLdapPermissionInfo( final PwmSetting pwmSetting )
  225. {
  226. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  227. final List<XmlElement> permissionElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_LDAP_PERMISSION );
  228. final List<LDAPPermissionInfo> returnObj = new ArrayList<>();
  229. if ( permissionElements != null )
  230. {
  231. for ( final XmlElement permissionElement : permissionElements )
  232. {
  233. final Optional<LDAPPermissionInfo.Actor> actor = JavaHelper.readEnumFromString(
  234. LDAPPermissionInfo.Actor.class,
  235. permissionElement.getAttribute( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACTOR ).orElse( "" )
  236. );
  237. final Optional<LDAPPermissionInfo.Access> type = JavaHelper.readEnumFromString(
  238. LDAPPermissionInfo.Access.class,
  239. permissionElement.getAttribute( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACCESS ).orElse( "" )
  240. );
  241. if ( actor.isPresent() && type.isPresent() )
  242. {
  243. final LDAPPermissionInfo permissionInfo = new LDAPPermissionInfo( type.get(), actor.get() );
  244. returnObj.add( permissionInfo );
  245. }
  246. }
  247. }
  248. return Collections.unmodifiableList( returnObj );
  249. }
  250. private static List<PwmSetting.TemplateSetReference<String>> readExamples( final PwmSetting pwmSetting )
  251. {
  252. final List<PwmSetting.TemplateSetReference<String>> returnObj = new ArrayList<>();
  253. final MacroRequest macroRequest = MacroRequest.forStatic();
  254. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  255. final List<XmlElement> exampleElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_EXAMPLE );
  256. for ( final XmlElement exampleElement : exampleElements )
  257. {
  258. final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( exampleElement );
  259. exampleElement.getText().ifPresent( textValue ->
  260. {
  261. final String exampleString = macroRequest.expandMacros( textValue );
  262. returnObj.add( new PwmSetting.TemplateSetReference<>( exampleString, Collections.unmodifiableSet( definedTemplates ) ) );
  263. } );
  264. }
  265. if ( returnObj.isEmpty() )
  266. {
  267. returnObj.add( new PwmSetting.TemplateSetReference<>( "", Collections.emptySet() ) );
  268. }
  269. return Collections.unmodifiableList( returnObj );
  270. }
  271. private static Map<PwmSettingProperty, String> readProperties( final PwmSetting pwmSetting )
  272. {
  273. final Map<PwmSettingProperty, String> newProps = new EnumMap<>( PwmSettingProperty.class );
  274. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  275. final Optional<XmlElement> propertiesElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_PROPERTIES );
  276. if ( propertiesElement.isPresent() )
  277. {
  278. final List<XmlElement> propertyElements = propertiesElement.get().getChildren( PwmSettingXml.XML_ELEMENT_PROPERTY );
  279. if ( propertyElements != null )
  280. {
  281. for ( final XmlElement propertyElement : propertyElements )
  282. {
  283. final String keyAttribute = propertyElement.getAttribute( PwmSettingXml.XML_ATTRIBUTE_KEY )
  284. .orElseThrow( () -> new IllegalStateException( "property element is missing 'key' attribute for value " + pwmSetting.getKey() ) );
  285. final PwmSettingProperty property = JavaHelper.readEnumFromString( PwmSettingProperty.class, keyAttribute )
  286. .orElseThrow( () -> new IllegalStateException( "property element has unknown 'key' attribute for value " + pwmSetting.getKey() ) );
  287. propertyElement.getText().ifPresent( value -> newProps.put( property, value ) );
  288. }
  289. }
  290. }
  291. return Collections.unmodifiableMap( newProps );
  292. }
  293. private static List<PwmSetting.TemplateSetReference<StoredValue>> readDefaultValue( final PwmSetting pwmSetting )
  294. {
  295. final List<PwmSetting.TemplateSetReference<StoredValue>> returnObj = new ArrayList<>();
  296. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  297. final List<XmlElement> defaultElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_DEFAULT );
  298. if ( pwmSetting.getSyntax() == PwmSettingSyntax.PASSWORD )
  299. {
  300. returnObj.add( new PwmSetting.TemplateSetReference<>( new PasswordValue( null ), Collections.emptySet() ) );
  301. }
  302. else
  303. {
  304. for ( final XmlElement defaultElement : defaultElements )
  305. {
  306. final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( defaultElement );
  307. final StoredValue storedValue = ValueFactory.fromXmlValues( pwmSetting, defaultElement, null );
  308. returnObj.add( new PwmSetting.TemplateSetReference<>( storedValue, definedTemplates ) );
  309. }
  310. }
  311. if ( returnObj.isEmpty() )
  312. {
  313. throw new IllegalStateException( "no default value for setting " + pwmSetting.getKey() );
  314. }
  315. return Collections.unmodifiableList( returnObj );
  316. }
  317. private static boolean readRequired( final PwmSetting pwmSetting )
  318. {
  319. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  320. final String requiredAttribute = settingElement.getAttribute( PwmSettingXml.XML_ELEMENT_REQUIRED ).orElse( "" );
  321. return "true".equalsIgnoreCase( requiredAttribute );
  322. }
  323. private static boolean readHidden( final PwmSetting pwmSetting )
  324. {
  325. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  326. final String requiredAttribute = settingElement.getAttribute( PwmSettingXml.XML_ELEMENT_HIDDEN ).orElse( "" );
  327. return "true".equalsIgnoreCase( requiredAttribute ) || pwmSetting.getCategory().isHidden();
  328. }
  329. private static int readLevel( final PwmSetting pwmSetting )
  330. {
  331. final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
  332. final String levelAttribute = settingElement.getAttribute( PwmSettingXml.XML_ELEMENT_LEVEL ).orElse( "" );
  333. return JavaHelper.silentParseInt( levelAttribute, 0 );
  334. }
  335. private static Pattern readPattern( final PwmSetting pwmSetting )
  336. {
  337. final XmlElement settingNode = PwmSettingXml.readSettingXml( pwmSetting );
  338. final Optional<XmlElement> regexNode = settingNode.getChild( PwmSettingXml.XML_ELEMENT_REGEX );
  339. if ( regexNode.isPresent() )
  340. {
  341. final Optional<String> regexText = regexNode.get().getText();
  342. if ( regexText.isPresent() )
  343. {
  344. try
  345. {
  346. return Pattern.compile( regexText.get() );
  347. }
  348. catch ( final PatternSyntaxException e )
  349. {
  350. final String errorMsg = "error compiling regex constraints for setting " + pwmSetting + ", error: " + e.getMessage();
  351. LOGGER.error( () -> errorMsg, e );
  352. throw new IllegalStateException( errorMsg, e );
  353. }
  354. }
  355. }
  356. return Pattern.compile( ".*", Pattern.DOTALL );
  357. }
  358. private static String readMenuLocationDebugDefault( final PwmSetting pwmSetting )
  359. {
  360. final Locale locale = PwmConstants.DEFAULT_LOCALE;
  361. final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
  362. return pwmSetting.getCategory().toMenuLocationDebug( null, locale ) + separator + pwmSetting.getLabel( locale );
  363. }
  364. private static String readMenuLocationDebug( final PwmSetting pwmSetting, final String profileID, final Locale locale )
  365. {
  366. final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
  367. return pwmSetting.getCategory().toMenuLocationDebug( profileID, locale ) + separator + pwmSetting.getLabel( locale );
  368. }
  369. private static String readLabel( final PwmSetting pwmSetting, final Locale locale )
  370. {
  371. return readStringProperty( password.pwm.i18n.PwmSetting.SETTING_LABEL_PREFIX + pwmSetting.getKey(), locale );
  372. }
  373. private static String readDescription( final PwmSetting pwmSetting, final Locale locale )
  374. {
  375. return readStringProperty( password.pwm.i18n.PwmSetting.SETTING_DESCRIPTION_PREFIX + pwmSetting.getKey(), locale );
  376. }
  377. private static String readStringProperty( final String key, final Locale locale )
  378. {
  379. final String storedText = LocaleHelper.getLocalizedMessage( locale, key, null, password.pwm.i18n.PwmSetting.class );
  380. final MacroRequest macroRequest = MacroRequest.forStatic();
  381. return macroRequest.expandMacros( storedText );
  382. }
  383. }
  384. }