MacroMachine.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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.util.macro;
  23. import password.pwm.PwmApplication;
  24. import password.pwm.PwmApplicationMode;
  25. import password.pwm.PwmConstants;
  26. import password.pwm.bean.LoginInfoBean;
  27. import password.pwm.bean.SessionLabel;
  28. import password.pwm.bean.UserIdentity;
  29. import password.pwm.config.PwmSetting;
  30. import password.pwm.error.PwmUnrecoverableException;
  31. import password.pwm.http.PwmRequest;
  32. import password.pwm.ldap.UserInfo;
  33. import password.pwm.ldap.UserInfoFactory;
  34. import password.pwm.util.java.JavaHelper;
  35. import password.pwm.util.logging.PwmLogger;
  36. import java.util.Collections;
  37. import java.util.HashSet;
  38. import java.util.LinkedHashMap;
  39. import java.util.List;
  40. import java.util.Locale;
  41. import java.util.Map;
  42. import java.util.Set;
  43. import java.util.regex.Matcher;
  44. import java.util.regex.Pattern;
  45. public class MacroMachine {
  46. private static final PwmLogger LOGGER = PwmLogger.forClass(MacroMachine.class);
  47. private final PwmApplication pwmApplication;
  48. private final SessionLabel sessionLabel;
  49. private final UserInfo userInfo;
  50. private final LoginInfoBean loginInfoBean;
  51. private static final Map<MacroImplementation.Scope,Map<Pattern,MacroImplementation>> BUILTIN_MACROS = makeImplementations();
  52. public MacroMachine(
  53. final PwmApplication pwmApplication,
  54. final SessionLabel sessionLabel,
  55. final UserInfo userInfo,
  56. final LoginInfoBean loginInfoBean
  57. )
  58. {
  59. this.pwmApplication = pwmApplication;
  60. this.sessionLabel = sessionLabel;
  61. this.userInfo = userInfo;
  62. this.loginInfoBean = loginInfoBean;
  63. }
  64. private static Map<MacroImplementation.Scope,Map<Pattern,MacroImplementation>> makeImplementations() {
  65. final Map<Class<? extends MacroImplementation>, MacroImplementation.Scope> implementations = new LinkedHashMap<>();
  66. implementations.putAll(StandardMacros.STANDARD_MACROS);
  67. implementations.putAll(InternalMacros.INTERNAL_MACROS);
  68. final LinkedHashMap<MacroImplementation.Scope,Map<Pattern,MacroImplementation>> map = new LinkedHashMap<>();
  69. for (final Class macroClass : implementations.keySet()) {
  70. final MacroImplementation.Scope scope = implementations.get(macroClass);
  71. try {
  72. final MacroImplementation macroImplementation = (MacroImplementation)macroClass.newInstance();
  73. final Pattern pattern = macroImplementation.getRegExPattern();
  74. if (!map.containsKey(scope)) {
  75. map.put(scope, new LinkedHashMap<>());
  76. }
  77. map.get(scope).put(pattern,macroImplementation);
  78. } catch (Exception e) {
  79. LOGGER.error("unable to load macro class " + macroClass.getName() + ", error: " + e.getMessage());
  80. }
  81. }
  82. return map;
  83. }
  84. private Map<Pattern,MacroImplementation> makeExternalImplementations(final PwmApplication pwmApplication) {
  85. final LinkedHashMap<Pattern,MacroImplementation> map = new LinkedHashMap<>();
  86. final List<String> externalMethods = (pwmApplication == null)
  87. ? Collections.emptyList()
  88. : pwmApplication.getConfig().readSettingAsStringArray(PwmSetting.EXTERNAL_MACROS_REST_URLS);
  89. int iteration = 0;
  90. for (final String url : externalMethods) {
  91. iteration++;
  92. final MacroImplementation macroImplementation = new ExternalRestMacro(iteration,url);
  93. final Pattern pattern = macroImplementation.getRegExPattern();
  94. map.put(pattern,macroImplementation);
  95. }
  96. return map;
  97. }
  98. public String expandMacros(
  99. final String input
  100. ) {
  101. return expandMacros(input, null);
  102. }
  103. public String expandMacros(
  104. final String input,
  105. final StringReplacer stringReplacer
  106. )
  107. {
  108. if (input == null) {
  109. return null;
  110. }
  111. if (input.length() < 1) {
  112. return input;
  113. }
  114. final MacroImplementation.MacroRequestInfo macroRequestInfo = new MacroImplementation.MacroRequestInfo() {
  115. @Override
  116. public PwmApplication getPwmApplication()
  117. {
  118. return pwmApplication;
  119. }
  120. @Override
  121. public UserInfo getUserInfo()
  122. {
  123. return userInfo;
  124. }
  125. @Override
  126. public LoginInfoBean getLoginInfoBean()
  127. {
  128. return loginInfoBean;
  129. }
  130. };
  131. final Set<MacroImplementation.Scope> scopes = effectiveScopes(macroRequestInfo);
  132. final Map<Pattern,MacroImplementation> macroImplementations = new LinkedHashMap<>();
  133. //First the User macros
  134. if (scopes.contains(MacroImplementation.Scope.User)) {
  135. macroImplementations.putAll(makeExternalImplementations(pwmApplication));
  136. }
  137. //last the buitin macros for Encrypt/Encode to work properly
  138. for (final MacroImplementation.Scope scope : scopes) {
  139. macroImplementations.putAll(BUILTIN_MACROS.get(scope));
  140. }
  141. String workingString = input;
  142. final String previousString = workingString;
  143. for (final MacroImplementation.Sequence sequence : MacroImplementation.Sequence.values()) {
  144. for (final Pattern pattern : macroImplementations.keySet()) {
  145. final MacroImplementation pwmMacro = macroImplementations.get(pattern);
  146. if (pwmMacro.getSequence() == sequence) {
  147. boolean matched = true;
  148. while (matched) {
  149. final Matcher matcher = pattern.matcher(workingString);
  150. if (matcher.find()) {
  151. workingString = doReplace(workingString, pwmMacro, matcher, stringReplacer, macroRequestInfo);
  152. if (workingString.equals(previousString)) {
  153. LOGGER.warn(sessionLabel, "macro replace was called but input string was not modified. "
  154. + " macro=" + pwmMacro.getClass().getName() + ", pattern=" + pwmMacro.getRegExPattern().toString());
  155. break;
  156. }
  157. } else {
  158. matched = false;
  159. }
  160. }
  161. }
  162. }
  163. }
  164. return workingString;
  165. }
  166. private static Set<MacroImplementation.Scope> effectiveScopes(final MacroImplementation.MacroRequestInfo macroRequestInfo) {
  167. final Set<MacroImplementation.Scope> scopes = new HashSet<>();
  168. scopes.add(MacroImplementation.Scope.Static);
  169. final PwmApplication pwmApplication = macroRequestInfo.getPwmApplication();
  170. final PwmApplicationMode mode = pwmApplication != null ? pwmApplication.getApplicationMode() : PwmApplicationMode.ERROR;
  171. final boolean appModeOk = mode == PwmApplicationMode.RUNNING || mode == PwmApplicationMode.CONFIGURATION;
  172. if (appModeOk) {
  173. scopes.add(MacroImplementation.Scope.System);
  174. if (macroRequestInfo.getUserInfo() != null) {
  175. scopes.add(MacroImplementation.Scope.User);
  176. }
  177. }
  178. return Collections.unmodifiableSet(scopes);
  179. }
  180. private String doReplace(
  181. final String input,
  182. final MacroImplementation macroImplementation,
  183. final Matcher matcher,
  184. final StringReplacer stringReplacer,
  185. final MacroImplementation.MacroRequestInfo macroRequestInfo
  186. ) {
  187. final String matchedStr = matcher.group();
  188. final int startPos = matcher.start();
  189. final int endPos = matcher.end();
  190. String replaceStr = "";
  191. try {
  192. replaceStr = macroImplementation.replaceValue(matchedStr, macroRequestInfo);
  193. } catch (MacroParseException e) {
  194. LOGGER.debug(sessionLabel, "macro parse error replacing macro '" + matchedStr + "', error: " + e.getMessage());
  195. if (pwmApplication != null) {
  196. replaceStr = "[" + e.getErrorInformation().toUserStr(PwmConstants.DEFAULT_LOCALE, macroRequestInfo.getPwmApplication().getConfig()) + "]";
  197. } else {
  198. replaceStr = "[" + e.getErrorInformation().toUserStr(PwmConstants.DEFAULT_LOCALE, null) + "]";
  199. }
  200. } catch (Exception e) {
  201. LOGGER.error(sessionLabel, "error while replacing macro '" + matchedStr + "', error: " + e.getMessage());
  202. }
  203. if (replaceStr == null) {
  204. return input;
  205. }
  206. if (stringReplacer != null) {
  207. try {
  208. replaceStr = stringReplacer.replace(matchedStr, replaceStr);
  209. } catch (Exception e) {
  210. LOGGER.error(sessionLabel,"unexpected error while executing '" + matchedStr + "' during StringReplacer.replace(), error: " + e.getMessage());
  211. }
  212. }
  213. if (replaceStr != null && replaceStr.length() > 0) {
  214. final boolean sensitive = JavaHelper.enumArrayContainsValue(macroImplementation.flags(), MacroImplementation.MacroDefinitionFlag.SensitiveValue);
  215. final boolean debugOnlyLogging = JavaHelper.enumArrayContainsValue(macroImplementation.flags(), MacroImplementation.MacroDefinitionFlag.OnlyDebugLogging);
  216. if (!debugOnlyLogging || (pwmApplication != null && pwmApplication.getConfig().isDevDebugMode())) {
  217. LOGGER.trace(sessionLabel, "replaced macro " + matchedStr + " with value: "
  218. + (sensitive ? PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT : replaceStr));
  219. }
  220. }
  221. return new StringBuilder(input).replace(startPos, endPos, replaceStr).toString();
  222. }
  223. public static MacroMachine forStatic() {
  224. return new MacroMachine(null,null,null,null);
  225. }
  226. public interface StringReplacer {
  227. String replace( String matchedMacro, String newValue);
  228. }
  229. public static MacroMachine forUser(
  230. final PwmRequest pwmRequest,
  231. final UserIdentity userIdentity
  232. )
  233. throws PwmUnrecoverableException
  234. {
  235. return forUser(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(),userIdentity);
  236. }
  237. public static MacroMachine forUser(
  238. final PwmApplication pwmApplication,
  239. final SessionLabel sessionLabel,
  240. final UserInfo userInfo,
  241. final LoginInfoBean loginInfoBean
  242. ) {
  243. return new MacroMachine(pwmApplication, sessionLabel, userInfo, loginInfoBean);
  244. }
  245. public static MacroMachine forUser(
  246. final PwmApplication pwmApplication,
  247. final Locale userLocale,
  248. final SessionLabel sessionLabel,
  249. final UserIdentity userIdentity
  250. )
  251. throws PwmUnrecoverableException
  252. {
  253. final UserInfo userInfoBean = UserInfoFactory.newUserInfoUsingProxy(pwmApplication, sessionLabel, userIdentity, userLocale);
  254. return new MacroMachine(pwmApplication, sessionLabel, userInfoBean, null);
  255. }
  256. public static MacroMachine forNonUserSpecific(
  257. final PwmApplication pwmApplication,
  258. final SessionLabel sessionLabel
  259. )
  260. throws PwmUnrecoverableException
  261. {
  262. return new MacroMachine(pwmApplication, sessionLabel, null, null);
  263. }
  264. }