Procházet zdrojové kódy

Merge branch 'ng-helpdesk' of github.com:pwm-project/pwm into ng-helpdesk

Joe Hawkins před 7 roky
rodič
revize
1ca827a69d
36 změnil soubory, kde provedl 797 přidání a 876 odebrání
  1. 6 239
      client/package-lock.json
  2. 72 72
      client/package.json
  3. 2 1
      client/src/helpdesk/main.ts
  4. 1 1
      client/src/helpdesk/routes.ts
  5. 2 1
      client/src/main.ts
  6. 3 3
      client/src/services/helpdesk.service.ts
  7. 20 1
      client/src/services/pwm.service.ts
  8. 7 0
      client/webpack.build.js
  9. 5 0
      server/pom.xml
  10. 4 0
      server/src/main/java/password/pwm/AppProperty.java
  11. 64 1
      server/src/main/java/password/pwm/health/ConfigurationChecker.java
  12. 1 0
      server/src/main/java/password/pwm/health/HealthMessage.java
  13. 1 0
      server/src/main/java/password/pwm/http/PwmRequestFlag.java
  14. 23 28
      server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java
  15. 1 1
      server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java
  16. 137 57
      server/src/main/java/password/pwm/svc/event/SyslogAuditService.java
  17. 4 0
      server/src/main/resources/password/pwm/AppProperty.properties
  18. 4 0
      server/src/main/resources/password/pwm/config/PwmSetting.xml
  19. 1 1
      server/src/main/resources/password/pwm/i18n/Config.properties
  20. 1 0
      server/src/main/resources/password/pwm/i18n/Health.properties
  21. 4 4
      server/src/main/resources/password/pwm/i18n/PwmSetting.properties
  22. 2 12
      server/src/main/webapp/WEB-INF/jsp/accountinformation.jsp
  23. 114 109
      server/src/main/webapp/WEB-INF/jsp/admin-activity.jsp
  24. 5 8
      server/src/main/webapp/WEB-INF/jsp/admin-analysis.jsp
  25. 37 39
      server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp
  26. 2 2
      server/src/main/webapp/WEB-INF/jsp/fragment/displayelement-row.jsp
  27. 9 0
      server/src/main/webapp/WEB-INF/jsp/fragment/footer.jsp
  28. 4 0
      server/src/main/webapp/WEB-INF/jsp/fragment/header.jsp
  29. 2 5
      server/src/main/webapp/WEB-INF/jsp/helpdesk.jsp
  30. 2 5
      server/src/main/webapp/WEB-INF/jsp/peoplesearch.jsp
  31. 1 13
      server/src/main/webapp/WEB-INF/jsp/setupotpsecret.jsp
  32. 131 158
      server/src/main/webapp/public/resources/js/admin.js
  33. 1 1
      server/src/main/webapp/public/resources/js/configeditor-settings.js
  34. 101 0
      server/src/main/webapp/public/resources/style.css
  35. 0 114
      server/src/main/webapp/public/resources/tab-container.css
  36. 23 0
      server/src/main/webapp/public/resources/themes/pwm/style.css

+ 6 - 239
client/package-lock.json

@@ -221,6 +221,12 @@
       "integrity": "sha512-6igWH2GIsxV+J38wNWCh8oyjaZsrIPIDO35twloIUyjlF2Yit6UyLAWujHP05ma/LFxTsx4NtYibRoMNBXPR1A==",
       "dev": true
     },
+    "angular-aria": {
+      "version": "1.6.9",
+      "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.6.9.tgz",
+      "integrity": "sha512-I2SR17Ux0o0R9KD3DRzjR5NNX3pUSKvtWYLFCLg22qvR5+736olCQSyptNIvKsvjALwfXBw1Irdlq0jyohDn+Q==",
+      "dev": true
+    },
     "angular-mocks": {
       "version": "1.6.9",
       "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.6.9.tgz",
@@ -444,13 +450,6 @@
         }
       }
     },
-    "async": {
-      "version": "0.2.10",
-      "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
-      "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
-      "dev": true,
-      "optional": true
-    },
     "async-each": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
@@ -2354,12 +2353,6 @@
         "source-map": "0.5.7"
       }
     },
-    "cubic2quad": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/cubic2quad/-/cubic2quad-1.1.1.tgz",
-      "integrity": "sha1-abGcYaP1tB7PLx1fro+wNBWqixU=",
-      "dev": true
-    },
     "currently-unhandled": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -3466,17 +3459,6 @@
         "debug": "2.6.7"
       }
     },
-    "fontgen-loader": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fontgen-loader/-/fontgen-loader-0.2.1.tgz",
-      "integrity": "sha1-uO1tmnmNWwVbgPHiHbSwQXC28FE=",
-      "dev": true,
-      "requires": {
-        "glob": "6.0.4",
-        "loader-utils": "0.2.17",
-        "webfonts-generator": "0.3.5"
-      }
-    },
     "for-in": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -4725,16 +4707,6 @@
       "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=",
       "dev": true
     },
-    "handlebars": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-2.0.0.tgz",
-      "integrity": "sha1-bp1/hRSjRn+l6fgswVjs/B1ax28=",
-      "dev": true,
-      "requires": {
-        "optimist": "0.3.7",
-        "uglify-js": "2.3.6"
-      }
-    },
     "har-schema": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
@@ -7066,12 +7038,6 @@
       "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
       "dev": true
     },
-    "microbuffer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/microbuffer/-/microbuffer-1.0.0.tgz",
-      "integrity": "sha1-izgy7UDIfVH0e7I0kTppinVtGdI=",
-      "dev": true
-    },
     "micromatch": {
       "version": "2.3.11",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
@@ -7342,15 +7308,6 @@
         "xml-char-classes": "1.0.0"
       }
     },
-    "neatequal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/neatequal/-/neatequal-1.0.0.tgz",
-      "integrity": "sha1-LuEhG8n6bkxVcV/SELsFYC6xrjs=",
-      "dev": true,
-      "requires": {
-        "varstream": "0.3.2"
-      }
-    },
     "negotiator": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@@ -7907,15 +7864,6 @@
         "is-wsl": "1.1.0"
       }
     },
-    "optimist": {
-      "version": "0.3.7",
-      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
-      "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
-      "dev": true,
-      "requires": {
-        "wordwrap": "0.0.3"
-      }
-    },
     "optionator": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
@@ -10897,18 +10845,6 @@
         "strip-ansi": "3.0.1"
       }
     },
-    "string.fromcodepoint": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz",
-      "integrity": "sha1-jZeDM8C8klOPUPOD5IiPPlYZ1lM=",
-      "dev": true
-    },
-    "string.prototype.codepointat": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz",
-      "integrity": "sha1-aybpvTr8qnvjtCabUm3huCAArHg=",
-      "dev": true
-    },
     "string_decoder": {
       "version": "0.10.31",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
@@ -11031,66 +10967,6 @@
         "has-flag": "1.0.0"
       }
     },
-    "svg-pathdata": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-1.0.4.tgz",
-      "integrity": "sha1-emgTQqrH7/2NUq+6eZmRDJ2juVk=",
-      "dev": true,
-      "requires": {
-        "readable-stream": "2.0.6"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.0.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
-          "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
-          "dev": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "1.0.0",
-            "process-nextick-args": "1.0.7",
-            "string_decoder": "0.10.31",
-            "util-deprecate": "1.0.2"
-          }
-        }
-      }
-    },
-    "svg2ttf": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/svg2ttf/-/svg2ttf-4.1.0.tgz",
-      "integrity": "sha1-ggIuVovQPBq7Zo/djRXwCJVqDhA=",
-      "dev": true,
-      "requires": {
-        "argparse": "1.0.9",
-        "cubic2quad": "1.1.1",
-        "lodash": "4.17.4",
-        "microbuffer": "1.0.0",
-        "svgpath": "2.2.1",
-        "xmldom": "0.1.27"
-      }
-    },
-    "svgicons2svgfont": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/svgicons2svgfont/-/svgicons2svgfont-5.0.2.tgz",
-      "integrity": "sha1-BRGCPGSRvhp9VDKS4pqK5ietBAY=",
-      "dev": true,
-      "requires": {
-        "commander": "2.11.0",
-        "neatequal": "1.0.0",
-        "readable-stream": "2.3.3",
-        "sax": "1.2.4",
-        "string.fromcodepoint": "0.2.1",
-        "string.prototype.codepointat": "0.2.0",
-        "svg-pathdata": "1.0.4"
-      }
-    },
     "svgo": {
       "version": "0.7.2",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
@@ -11106,12 +10982,6 @@
         "whet.extend": "0.9.9"
       }
     },
-    "svgpath": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.2.1.tgz",
-      "integrity": "sha1-CDS7Z8iadkcrK9BswQH6e1F7Iiw=",
-      "dev": true
-    },
     "syntax-error": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz",
@@ -11791,27 +11661,6 @@
         "tslib": "1.9.0"
       }
     },
-    "ttf2eot": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ttf2eot/-/ttf2eot-2.0.0.tgz",
-      "integrity": "sha1-jmM3pYWr0WCKDISVirSDzmn2ZUs=",
-      "dev": true,
-      "requires": {
-        "argparse": "1.0.9",
-        "microbuffer": "1.0.0"
-      }
-    },
-    "ttf2woff": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/ttf2woff/-/ttf2woff-2.0.1.tgz",
-      "integrity": "sha1-hxgyJAAksJ25VwkEx8GSi4BXyWk=",
-      "dev": true,
-      "requires": {
-        "argparse": "1.0.9",
-        "microbuffer": "1.0.0",
-        "pako": "1.0.5"
-      }
-    },
     "tty-browserify": {
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@@ -11865,30 +11714,6 @@
       "integrity": "sha512-bqB1yS6o9TNA9ZC/MJxM0FZzPnZdtHj0xWK/IZ5khzVqdpGul/R/EIiHRgFXlwTD7PSIaYVnGKq1QgMCu2mnqw==",
       "dev": true
     },
-    "uglify-js": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz",
-      "integrity": "sha1-+gmEdwtCi3qbKoBY9GNV0U/vIRo=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "async": "0.2.10",
-        "optimist": "0.3.7",
-        "source-map": "0.1.43"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.1.43",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
-          "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "amdefine": "1.0.1"
-          }
-        }
-      }
-    },
     "uglify-to-browserify": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
@@ -11926,12 +11751,6 @@
       "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=",
       "dev": true
     },
-    "underscore": {
-      "version": "1.8.3",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
-      "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
-      "dev": true
-    },
     "union-value": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@@ -12091,12 +11910,6 @@
       "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
       "dev": true
     },
-    "url-join": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz",
-      "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=",
-      "dev": true
-    },
     "url-loader": {
       "version": "0.6.2",
       "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz",
@@ -12307,29 +12120,6 @@
         "spdx-expression-parse": "1.0.4"
       }
     },
-    "varstream": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/varstream/-/varstream-0.3.2.tgz",
-      "integrity": "sha1-GKxklHZfP/GjWtmkvgU77BiKXeE=",
-      "dev": true,
-      "requires": {
-        "readable-stream": "1.1.14"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "1.1.14",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
-          "dev": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "0.0.1",
-            "string_decoder": "0.10.31"
-          }
-        }
-      }
-    },
     "vary": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
@@ -12385,23 +12175,6 @@
         "minimalistic-assert": "1.0.0"
       }
     },
-    "webfonts-generator": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/webfonts-generator/-/webfonts-generator-0.3.5.tgz",
-      "integrity": "sha1-4t7/t4ZEhOn1qTpYYHp2dSxp2ig=",
-      "dev": true,
-      "requires": {
-        "handlebars": "2.0.0",
-        "mkdirp": "0.5.1",
-        "q": "1.5.0",
-        "svg2ttf": "4.1.0",
-        "svgicons2svgfont": "5.0.2",
-        "ttf2eot": "2.0.0",
-        "ttf2woff": "2.0.1",
-        "underscore": "1.8.3",
-        "url-join": "1.1.0"
-      }
-    },
     "webpack": {
       "version": "3.10.0",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz",
@@ -13644,12 +13417,6 @@
       "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=",
       "dev": true
     },
-    "xmldom": {
-      "version": "0.1.27",
-      "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
-      "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
-      "dev": true
-    },
     "xregexp": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",

+ 72 - 72
client/package.json

@@ -1,74 +1,74 @@
 {
-  "name": "ng1-webpack",
-  "version": "0.0.1",
-  "description": "",
-  "main": "index.js",
-  "engines": {
-    "node": ">=6.2",
-    "npm": ">=3.9"
-  },
-  "scripts": {
-    "build": "webpack --config=webpack.build.js --NODE_ENV=production",
-    "clean": "rimraf dist/",
-    "test": "karma start --NODE_ENV=test",
-    "test-single-run": "karma start --NODE_ENV=test --singleRun --no-auto-watch",
-    "start": "webpack-dev-server --config=webpack.dev.js --NODE_ENV=dev --colors",
-    "sync": "webpack --config=webpack.build.js --NODE_ENV=production --output-path=../server/src/main/webapp/public/resources/webjars/pwm-client --watch --colors"
-  },
-  "author": "",
-  "license": "ISC",
-  "dependencies": {},
-  "devDependencies": {
-    "@types/angular": "1.6.42",
-    "@types/angular-mocks": "1.5.11",
-    "@types/angular-translate": "2.15.1",
-    "@types/angular-ui-router": "1.1.40",
-    "@types/jasmine": "2.8.6",
-    "@types/node": "9.4.2",
-    "@uirouter/angularjs": "1.0.14",
-    "angular": "1.6.9",
-    "angular-aria": "1.6.9",
-    "angular-mocks": "1.6.9",
-    "angular-translate": "2.17.0",
-    "autoprefixer": "7.2.5",
-    "copy-webpack-plugin": "4.4.1",
-    "css-loader": "0.28.9",
-    "file-loader": "1.1.6",
-    "html-loader": "0.5.5",
-    "html-webpack-plugin": "2.30.1",
-    "ignore-loader": "0.1.2",
-    "jasmine": "3.0.0",
-    "jasmine-core": "^2.99.1",
-    "jshint": "2.9.5",
-    "jshint-loader": "0.8.4",
-    "json-loader": "0.5.7",
-    "karma": "2.0.0",
-    "karma-chrome-launcher": "2.2.0",
-    "karma-jasmine": "1.1.1",
-    "karma-jasmine-html-reporter": "0.2.2",
-    "karma-phantomjs-launcher": "1.0.4",
-    "karma-sourcemap-loader": "0.3.7",
-    "karma-spec-reporter": "0.0.32",
-    "karma-webpack": "2.0.9",
-    "ngtemplate-loader": "2.0.1",
-    "node-sass": "4.7.2",
-    "phantomjs": "2.1.7",
-    "phantomjs-prebuilt": "2.1.16",
-    "postcss-loader": "2.1.0",
-    "raw-loader": "0.5.1",
-    "rimraf": "2.6.2",
-    "sass-loader": "6.0.6",
-    "string-replace-loader": "1.3.0",
-    "style-loader": "0.20.1",
-    "ts-loader": "3.5.0",
-    "ts-mockito": "2.2.9",
-    "tslint": "^5.9.1",
-    "tslint-loader": "3.5.3",
-    "typescript": "2.7.1",
-    "url-loader": "0.6.2",
-    "webpack": "3.10.0",
-    "webpack-dev-server": "2.11.1",
-    "webpack-merge": "4.1.1",
-    "write-file-webpack-plugin": "4.2.0"
-  }
+    "name": "ng1-webpack",
+    "version": "0.0.1",
+    "description": "",
+    "main": "index.js",
+    "engines": {
+        "node": ">=6.2",
+        "npm": ">=3.9"
+    },
+    "scripts": {
+        "build": "webpack --config=webpack.build.js --env.NODE_ENV=production",
+        "clean": "rimraf dist/",
+        "test": "karma start --env.NODE_ENV=test",
+        "test-single-run": "karma start --env.NODE_ENV=test --singleRun --no-auto-watch",
+        "start": "webpack-dev-server --config=webpack.dev.js --env.NODE_ENV=dev --colors",
+        "sync": "webpack --config=webpack.build.js --env.NODE_ENV=production --output-path=../server/target/pwm-1.8.0-SNAPSHOT/public/resources/webjars/pwm-client --watch --colors"
+    },
+    "author": "",
+    "license": "ISC",
+    "dependencies": {},
+    "devDependencies": {
+        "@types/angular": "1.6.42",
+        "@types/angular-mocks": "1.5.11",
+        "@types/angular-translate": "2.15.1",
+        "@types/angular-ui-router": "1.1.40",
+        "@types/jasmine": "2.8.6",
+        "@types/node": "9.4.2",
+        "@uirouter/angularjs": "1.0.14",
+        "angular": "1.6.9",
+        "angular-aria": "1.6.9",
+        "angular-mocks": "1.6.9",
+        "angular-translate": "2.17.0",
+        "autoprefixer": "7.2.5",
+        "copy-webpack-plugin": "4.4.1",
+        "css-loader": "0.28.9",
+        "file-loader": "1.1.6",
+        "html-loader": "0.5.5",
+        "html-webpack-plugin": "2.30.1",
+        "ignore-loader": "0.1.2",
+        "jasmine": "3.0.0",
+        "jasmine-core": "2.99.1",
+        "jshint": "2.9.5",
+        "jshint-loader": "0.8.4",
+        "json-loader": "0.5.7",
+        "karma": "2.0.0",
+        "karma-chrome-launcher": "2.2.0",
+        "karma-jasmine": "1.1.1",
+        "karma-jasmine-html-reporter": "0.2.2",
+        "karma-phantomjs-launcher": "1.0.4",
+        "karma-sourcemap-loader": "0.3.7",
+        "karma-spec-reporter": "0.0.32",
+        "karma-webpack": "2.0.9",
+        "ngtemplate-loader": "2.0.1",
+        "node-sass": "4.7.2",
+        "phantomjs": "2.1.7",
+        "phantomjs-prebuilt": "2.1.16",
+        "postcss-loader": "2.1.0",
+        "raw-loader": "0.5.1",
+        "rimraf": "2.6.2",
+        "sass-loader": "6.0.6",
+        "string-replace-loader": "1.3.0",
+        "style-loader": "0.20.1",
+        "ts-loader": "3.5.0",
+        "ts-mockito": "2.2.9",
+        "tslint": "5.9.1",
+        "tslint-loader": "3.5.3",
+        "typescript": "2.7.1",
+        "url-loader": "0.6.2",
+        "webpack": "3.10.0",
+        "webpack-dev-server": "2.11.1",
+        "webpack-merge": "4.1.1",
+        "write-file-webpack-plugin": "4.2.0"
+    }
 }

+ 2 - 1
client/src/helpdesk/main.ts

@@ -34,7 +34,8 @@ import HelpDeskService from '../services/helpdesk.service';
 module('app', [
     uiRouter,
     helpDeskModule,
-    'pascalprecht.translate'
+    'pascalprecht.translate',
+    'ng-ias'
 ])
     .config(routes)
     .config([

+ 1 - 1
client/src/helpdesk/routes.ts

@@ -30,7 +30,7 @@ export default [
     ) => {
         $urlRouterProvider.otherwise(
             ($injector: angular.auto.IInjectorService, $location: angular.ILocationService) => {
-                $location.url('search');
+                $location.url('search/cards');
             });
 
         $stateProvider.state('search', {

+ 2 - 1
client/src/main.ts

@@ -34,7 +34,8 @@ import uiRouter from '@uirouter/angularjs';
 module('app', [
     uiRouter,
     peopleSearchModule,
-    'pascalprecht.translate'
+    'pascalprecht.translate',
+    'ng-ias'
 ])
 
     .config(routes)

+ 3 - 3
client/src/services/helpdesk.service.ts

@@ -118,8 +118,8 @@ export default class HelpDeskService implements IHelpDeskService {
 
         return this.pwmService
             .httpRequest(url, { data: data })
-            .then((result: IVerificationStatus) => {
-                return this.$q.resolve(result);
+            .then((result: any) => {
+                return this.$q.resolve(result.data);
             });
     }
 
@@ -180,7 +180,7 @@ export default class HelpDeskService implements IHelpDeskService {
         return this.pwmService
             .httpRequest(url, {})
             .then((result: any) => {
-                return this.$q.resolve(result);
+                return this.$q.resolve(result.data);
             });
     }
 

+ 20 - 1
client/src/services/pwm.service.ts

@@ -111,7 +111,26 @@ export default class PwmService implements IPwmService {
                     return this.handlePwmError(response);
                 }
 
-                return this.$q.resolve(response.data['data']);
+                // Note: sometimes response.data looks like this:
+                // {
+                //     "error": false,
+                //     "errorCode": 0,
+                //     "data": {
+                //         "foo": "1",
+                //         "bar": "2"
+                //     }
+                // }
+
+                // Note: other times, response.data looks like this:
+                // {
+                //     "error": false,
+                //     "errorCode": 0,
+                //     "successMessage": "The operation has been successfully completed."
+                // }
+
+                // Since we can't make assumptions about the structure, we just need to return the whole response.data
+                // payload:
+                return this.$q.resolve(response.data);
             });
 
         return promise;

+ 7 - 0
client/webpack.build.js

@@ -22,6 +22,7 @@
 
 
 var commonConfig = require('./webpack.common.js');
+var CopyWebpackPlugin = require('copy-webpack-plugin');
 var webpack = require('webpack');
 var webpackMerge = require('webpack-merge');
 
@@ -34,6 +35,12 @@ module.exports = webpackMerge(commonConfig, {
         'helpdesk.ng': './src/helpdesk/main'
     },
     plugins: [
+        new CopyWebpackPlugin([
+            { from: 'node_modules/@microfocus/ux-ias/dist/ux-ias.css', to: 'vendor/' },
+            { from: 'node_modules/@microfocus/ng-ias/dist/ng-ias.js', to: 'vendor/' },
+            { from: 'node_modules/@microfocus/ias-icons/dist/ias-icons.css', to: 'vendor/' },
+            { from: 'node_modules/@microfocus/ias-icons/dist/fonts', to: 'vendor/fonts' }
+        ]),
         new webpack.optimize.UglifyJsPlugin({
             compress: { warnings: false },
             comments: false,

+ 5 - 0
server/pom.xml

@@ -836,6 +836,11 @@
             <artifactId>angular</artifactId>
             <version>1.6.5</version>
         </dependency>
+        <dependency>
+            <groupId>org.webjars.bower</groupId>
+            <artifactId>angular-aria</artifactId>
+            <version>1.6.9</version>
+        </dependency>
         <dependency>
             <groupId>org.webjars.npm</groupId>
             <artifactId>angular-ui-router</artifactId>

+ 4 - 0
server/src/main/java/password/pwm/AppProperty.java

@@ -38,6 +38,10 @@ public enum AppProperty
     AUDIT_EVENTS_EMAILFROM                          ( "audit.events.emailFrom" ),
     AUDIT_EVENTS_EMAILSUBJECT                       ( "audit.events.emailSubject" ),
     AUDIT_EVENTS_LOCALDB_MAX_BULK_REMOVALS          ( "audit.events.localdb.maxBulkRemovals" ),
+    AUDIT_SYSLOG_CEF_EXTENSIONS                     ( "audit.syslog.cef.extensions" ),
+    AUDIT_SYSLOG_CEF_HEADER_PRODUCT                 ( "audit.syslog.cef.header.product" ),
+    AUDIT_SYSLOG_CEF_HEADER_SEVERITY                ( "audit.syslog.cef.header.severity" ),
+    AUDIT_SYSLOG_CEF_HEADER_VENDOR                  ( "audit.syslog.cef.header.vendor" ),
     AUDIT_SYSLOG_MAX_MESSAGE_LENGTH                 ( "audit.syslog.message.length" ),
     AUDIT_SYSLOG_TRUNCATE_MESSAGE                   ( "audit.syslog.message.truncateMsg" ),
     BACKUP_LOCATION                                 ( "backup.path" ),

+ 64 - 1
server/src/main/java/password/pwm/health/ConfigurationChecker.java

@@ -30,6 +30,7 @@ import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingSyntax;
+import password.pwm.config.StoredValue;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.option.MessageSendMethod;
 import password.pwm.config.profile.ForgottenPasswordProfile;
@@ -37,11 +38,13 @@ import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.profile.NewUserProfile;
 import password.pwm.config.profile.PwmPasswordPolicy;
+import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.Config;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.PasswordData;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.operations.PasswordUtility;
 
@@ -273,7 +276,8 @@ public class ConfigurationChecker implements HealthChecker
             VerifyPasswordPolicyConfigs.class,
             VerifyResponseLdapAttribute.class,
             VerifyDbConfiguredIfNeeded.class,
-            VerifyIfDeprecatedSendMethodValuesUsed.class
+            VerifyIfDeprecatedSendMethodValuesUsed.class,
+            VerifyIfDeprecatedJsFormOptionUsed.class
     ) );
 
     static class VerifyResponseLdapAttribute implements ConfigHealthCheck
@@ -365,6 +369,65 @@ public class ConfigurationChecker implements HealthChecker
         }
     }
 
+    static class VerifyIfDeprecatedJsFormOptionUsed implements ConfigHealthCheck
+    {
+        @Override
+        public List<HealthRecord> healthCheck( final Configuration config, final Locale locale )
+        {
+            final List<HealthRecord> records = new ArrayList<>();
+
+            for ( final PwmSetting loopSetting : PwmSetting.values() )
+            {
+                if ( loopSetting.getSyntax() == PwmSettingSyntax.FORM )
+                {
+                    if ( loopSetting.getCategory().hasProfiles() )
+                    {
+                        try
+                        {
+                            final List<String> profiles = config.getStoredConfiguration().profilesForSetting( loopSetting );
+                            for ( final String profile : profiles )
+                            {
+                                final StoredValue storedValue = config.getStoredConfiguration().readSetting( loopSetting, profile );
+                                final List<FormConfiguration> forms = (List<FormConfiguration>) storedValue.toNativeObject();
+                                for ( final FormConfiguration form : forms )
+                                {
+                                    if ( !StringUtil.isEmpty( form.getJavascript() ) )
+                                    {
+                                        records.add( HealthRecord.forMessage(
+                                                HealthMessage.Config_DeprecatedJSForm,
+                                                loopSetting.toMenuLocationDebug( profile, locale ),
+                                                PwmSetting.DISPLAY_CUSTOM_JAVASCRIPT.toMenuLocationDebug( null, locale )
+                                        ) );
+                                    }
+                                }
+                            }
+                        }
+                        catch ( PwmUnrecoverableException e )
+                        {
+                            LOGGER.error( "unexpected error examining profiles for deprecated form  js option check: " + e.getMessage() );
+                        }
+                    }
+                    else
+                    {
+                        final List<FormConfiguration> forms = config.readSettingAsForm( loopSetting );
+                        for ( final FormConfiguration form : forms )
+                        {
+                            if ( !StringUtil.isEmpty( form.getJavascript() ) )
+                            {
+                                records.add( HealthRecord.forMessage(
+                                        HealthMessage.Config_DeprecatedJSForm,
+                                        loopSetting.toMenuLocationDebug( null, locale ),
+                                        PwmSetting.DISPLAY_CUSTOM_JAVASCRIPT.toMenuLocationDebug( null, locale )
+                                ) );
+                            }
+                        }
+                    }
+                }
+            }
+            return records;
+        }
+    }
+
     static class VerifyIfDeprecatedSendMethodValuesUsed implements ConfigHealthCheck
     {
         @Override

+ 1 - 0
server/src/main/java/password/pwm/health/HealthMessage.java

@@ -69,6 +69,7 @@ public enum HealthMessage
     Config_NoRecoveryEnabled( HealthStatus.CAUTION, HealthTopic.Configuration ),
     Config_Certificate( HealthStatus.WARN, HealthTopic.Configuration ),
     Config_InvalidSendMethod( HealthStatus.CAUTION, HealthTopic.Configuration ),
+    Config_DeprecatedJSForm( HealthStatus.CONFIG, HealthTopic.Configuration ),
     LDAP_VendorsNotSame( HealthStatus.CONFIG, HealthTopic.LDAP ),
     LDAP_OK( HealthStatus.GOOD, HealthTopic.LDAP ),
     LDAP_RecentlyUnreachable( HealthStatus.CAUTION, HealthTopic.LDAP ),

+ 1 - 0
server/src/main/java/password/pwm/http/PwmRequestFlag.java

@@ -34,4 +34,5 @@ public enum PwmRequestFlag
     NO_MOBILE_CSS,
     ALWAYS_EXPAND_MESSAGE_TEXT,
     INCLUDE_CONFIG_CSS,
+    INCLUDE_IAS_ANGULAR,
 }

+ 23 - 28
server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java

@@ -48,10 +48,8 @@ import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
-import password.pwm.svc.event.HelpdeskAuditRecord;
-import password.pwm.svc.event.SystemAuditRecord;
-import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.report.ReportColumnFilter;
 import password.pwm.svc.report.ReportCsvUtility;
@@ -62,6 +60,7 @@ import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.reports.ReportUtils;
@@ -75,6 +74,7 @@ import java.io.OutputStream;
 import java.io.Writer;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -84,6 +84,7 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
 
 @WebServlet(
         name = "AdminServlet",
@@ -437,38 +438,32 @@ public class AdminServlet extends ControlledPwmServlet
 
     @ActionHandler( action = "auditData" )
     private ProcessStatus restAuditDataHandler( final PwmRequest pwmRequest )
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException
+            throws PwmUnrecoverableException, IOException
     {
-        final int max = readMaxParameter( pwmRequest, 1000, 10 * 1000 );
-        final ArrayList<UserAuditRecord> userRecords = new ArrayList<>();
-        final ArrayList<HelpdeskAuditRecord> helpdeskRecords = new ArrayList<>();
-        final ArrayList<SystemAuditRecord> systemRecords = new ArrayList<>();
+        final Instant startTime = Instant.now();
+        final TimeDuration maxSearchTime = new TimeDuration( 10, TimeUnit.SECONDS );
+        final int max = readMaxParameter( pwmRequest, 100, 10 * 1000 );
+        final AuditEvent.Type auditDataType = AuditEvent.Type.valueOf( pwmRequest.readParameterAsString( "type", AuditEvent.Type.USER.name() ) );
+        final ArrayList<AuditRecord> records = new ArrayList<>();
         final Iterator<AuditRecord> iterator = pwmRequest.getPwmApplication().getAuditManager().readVault();
-        int counter = 0;
-        while ( iterator.hasNext() && counter <= max )
+
+        while (
+                iterator.hasNext()
+                        && records.size() < max
+                        && TimeDuration.fromCurrent( startTime ).isShorterThan( maxSearchTime )
+                )
         {
             final AuditRecord loopRecord = iterator.next();
-            counter++;
-            if ( loopRecord instanceof SystemAuditRecord )
+            if ( auditDataType == loopRecord.getType() )
             {
-                systemRecords.add( ( SystemAuditRecord ) loopRecord );
-            }
-            else if ( loopRecord instanceof HelpdeskAuditRecord )
-            {
-                helpdeskRecords.add( ( HelpdeskAuditRecord ) loopRecord );
-            }
-            else if ( loopRecord instanceof UserAuditRecord )
-            {
-                userRecords.add( ( UserAuditRecord ) loopRecord );
+                records.add( loopRecord );
             }
         }
-        final HashMap<String, List> outputMap = new HashMap<>();
-        outputMap.put( "user", userRecords );
-        outputMap.put( "helpdesk", helpdeskRecords );
-        outputMap.put( "system", systemRecords );
 
-        final RestResultBean restResultBean = RestResultBean.withData( outputMap );
-        LOGGER.debug( pwmRequest.getPwmSession(), "output " + counter + " audit records." );
+        final HashMap<String, Object> resultData = new HashMap<>( Collections.singletonMap( "records", records ) );
+
+        final RestResultBean restResultBean = RestResultBean.withData( resultData );
+        LOGGER.debug( pwmRequest.getPwmSession(), "output " + records.size() + " audit records." );
         pwmRequest.outputJsonResult( restResultBean );
         return ProcessStatus.Halt;
     }
@@ -640,7 +635,7 @@ public class AdminServlet extends ControlledPwmServlet
             throws PwmUnrecoverableException
     {
         final String stringMax = pwmRequest.readParameterAsString( "maximum", String.valueOf( defaultValue ) );
-        return Math.max( Integer.parseInt( stringMax ), maxValue );
+        return Math.min( Integer.parseInt( stringMax ), maxValue );
     }
 
     public enum Page

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -480,7 +480,7 @@ public class AppDashboardData implements Serializable
             javaInfo.add( new DisplayElement(
                     "sessionAverageSize",
                     DisplayElement.Type.string,
-                    "Estimated Session Total Size",
+                    "Estimated Session Average Size",
                     debugInfoMap.get( SessionTrackService.DebugKey.HttpSessionAvgSize )
             ) );
         }

+ 137 - 57
server/src/main/java/password/pwm/svc/event/SyslogAuditService.java

@@ -40,25 +40,30 @@ import org.graylog2.syslog4j.impl.net.udp.UDPNetSyslogConfig;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.SyslogOutputFormat;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthStatus;
 import password.pwm.health.HealthTopic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBStoredQueue;
 import password.pwm.util.localdb.WorkQueueProcessor;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.secure.X509Utils;
 
 import javax.net.SocketFactory;
@@ -69,8 +74,11 @@ import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 
 public class SyslogAuditService
@@ -81,6 +89,17 @@ public class SyslogAuditService
     private static final String SYSLOG_INSTANCE_NAME = "syslog-audit";
     private static final int LENGTH_OVERSIZE = 1024;
 
+    private static final Map<String, String> CEF_VALUE_ESCAPES;
+
+
+    static
+    {
+        final Map<String, String> map = new LinkedHashMap<>( );
+        map.put( "\\", "\\\\" );
+        map.put( "=", "\\=" );
+        map.put( "|", "\"" );
+        CEF_VALUE_ESCAPES = Collections.unmodifiableMap( map );
+    }
 
     private SyslogIF syslogInstance = null;
     private ErrorInformation lastError = null;
@@ -92,6 +111,7 @@ public class SyslogAuditService
     private final Configuration configuration;
     private final PwmApplication pwmApplication;
     private final SyslogOutputFormat syslogOutputFormat;
+    private final Map<String, String> syslogCefExtensions;
 
     public SyslogAuditService( final PwmApplication pwmApplication )
             throws LocalDBException
@@ -100,6 +120,7 @@ public class SyslogAuditService
         this.pwmApplication = pwmApplication;
         this.configuration = pwmApplication.getConfig();
         this.certificates = configuration.readSettingAsCertificate( PwmSetting.AUDIT_SYSLOG_CERTIFICATES );
+        this.syslogCefExtensions = figureCefSyslogExtensions( configuration );
 
         final List<String> syslogConfigStringArray = configuration.readSettingAsStringArray( PwmSetting.AUDIT_SYSLOG_SERVERS );
         try
@@ -196,21 +217,31 @@ public class SyslogAuditService
 
         final String syslogMsg;
 
-        switch ( syslogOutputFormat )
+        try
         {
-            case JSON:
-                syslogMsg = convertAuditRecordToSyslogMessage( event, configuration );
-                break;
+            switch ( syslogOutputFormat )
+            {
+                case JSON:
+                    syslogMsg = convertAuditRecordToSyslogMessage( event, configuration );
+                    break;
 
-            case CEF:
-                syslogMsg = convertAuditRecordToCEFMessage( event, configuration );
-                break;
+                case CEF:
+                    syslogMsg = convertAuditRecordToCEFMessage( event, configuration );
+                    break;
 
-            default:
-                JavaHelper.unhandledSwitchStatement( syslogOutputFormat );
-                throw new IllegalStateException();
+                default:
+                    JavaHelper.unhandledSwitchStatement( syslogOutputFormat );
+                    throw new IllegalStateException();
+            }
+        }
+        catch ( PwmUnrecoverableException e )
+        {
+            final String msg = "error generating syslog message text: " + e.getMessage();
+            final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_SYSLOG_WRITE_ERROR, msg );
+            throw new PwmOperationalException( errorInfo );
         }
 
+
         try
         {
             workQueueProcessor.submit( syslogMsg );
@@ -267,7 +298,10 @@ public class SyslogAuditService
     public void close( )
     {
         final SyslogIF syslogIF = syslogInstance;
-        syslogIF.shutdown();
+        if ( syslogIF != null )
+        {
+            syslogIF.shutdown();
+        }
         workQueueProcessor.close();
         syslogInstance = null;
     }
@@ -331,58 +365,93 @@ public class SyslogAuditService
         return message.toString();
     }
 
-    private static String convertAuditRecordToCEFMessage( final AuditRecord auditRecord, final Configuration configuration )
+    private String convertAuditRecordToCEFMessage(
+            final AuditRecord auditRecord,
+            final Configuration configuration
+    )
+            throws PwmUnrecoverableException
     {
+        final Map<String, Object> auditRecordMap = JsonUtil.deserializeMap( JsonUtil.serialize( auditRecord ) );
+        final String separator = "|";
 
-        final String recordType = auditRecord.getType().name();
-        String recordString = "";
-        String translatedString = "";
-        if ( "USER".equalsIgnoreCase( recordType ) )
-        {
-            final UserAuditRecord cefRecord = new UserAuditRecord( auditRecord.timestamp, auditRecord.eventCode, null, null, null,
-                    auditRecord.message, null, null );
-            recordString = JsonUtil.serialize( cefRecord );
-        }
-        else if ( "SYSTEM".equalsIgnoreCase( recordType ) )
-        {
-            final SystemAuditRecord cefRecord = new SystemAuditRecord( auditRecord.eventCode, auditRecord.message, null );
-            recordString = JsonUtil.serialize( cefRecord );
-        }
-        else if ( "HELPDESK".equalsIgnoreCase( recordType ) )
+        final String headerSeverity = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_SEVERITY );
+        final String headerProduct = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_PRODUCT );
+        final String headerVendor = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_VENDOR );
+
+
+        final MacroMachine macroMachine = MacroMachine.forNonUserSpecific( pwmApplication, SessionLabel.SYSTEM_LABEL );
+
+        final String auditFieldName = LocaleHelper.getLocalizedMessage(
+                PwmConstants.DEFAULT_LOCALE,
+                auditRecord.getEventCode().getMessage(),
+                configuration
+        );
+
+        final StringBuilder cefOutput = new StringBuilder(  );
+
+        // cef declaration:version prefix
+        cefOutput.append( "CEF:0" );
+
+        // Device Vendor
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerVendor ) );
+
+        // Device Product
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerProduct ) );
+
+        // Device Version
+        cefOutput.append( separator );
+        cefOutput.append( PwmConstants.SERVLET_VERSION );
+
+        // Device Event Class ID
+        cefOutput.append( separator );
+        cefOutput.append( auditRecord.getEventCode() );
+
+        // field name
+        cefOutput.append( separator );
+        cefOutput.append( auditFieldName );
+
+        // severity
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerSeverity ) );
+
+        boolean extensionAdded = false;
+        for ( final Map.Entry<String, String> entry : syslogCefExtensions.entrySet() )
         {
-            final HelpdeskAuditRecord cefRecord = new HelpdeskAuditRecord( auditRecord.timestamp, auditRecord.eventCode, null, null, null,
-                    auditRecord.message, null, null, null, null, null );
-            recordString = JsonUtil.serialize( cefRecord );
+            final Object value = auditRecordMap.get( entry.getKey() );
+            if ( value != null )
+            {
+                if ( !extensionAdded )
+                {
+                    cefOutput.append( separator );
+                    extensionAdded = true;
+                }
+                else
+                {
+                    cefOutput.append( " " );
+                }
+                cefOutput.append( entry.getValue() );
+                cefOutput.append( "=" );
+                cefOutput.append( escapeCEFValue( value.toString() ) );
+            }
         }
-        else
+
+        return cefOutput.toString();
+    }
+
+
+
+    private static String escapeCEFValue( final String value )
+    {
+        String replacedValue = value;
+        for ( final Map.Entry<String, String> entry : CEF_VALUE_ESCAPES.entrySet() )
         {
-            recordString = JsonUtil.serialize( auditRecord );
+            final String pattern = entry.getKey();
+            final String replacement = entry.getValue();
+            replacedValue = replacedValue.replace( pattern, replacement );
         }
-        recordString = recordString.replace( "\"", "" );
-        recordString = recordString.replace( "\\", "" );
-        recordString = recordString.replace( "{", "" );
-        recordString = recordString.replace( "}", "" );
-
-        recordString = recordString.replace( "type:", " cat | " );
-        recordString = recordString.replace( "eventCode:", " act | " );
-        recordString = recordString.replace( "timestamp:", " rt | " );
-        recordString = recordString.replace( "message:", " msg | " );
-        recordString = recordString.replace( "narrative:", " reason | " );
-        recordString = recordString.replace( "perpetratorID:", " suid | " );
-        recordString = recordString.replace( "perpetratorDN:", " suser | " );
-        recordString = recordString.replace( "sourceAddress:", " dvc | " );
-        recordString = recordString.replace( "sourceHost:", " dvchost | " );
-        recordString = recordString.replace( "targetID:", " duid | " );
-        recordString = recordString.replace( "targetDN:", " duser | " );
-        recordString = recordString.replace( "SSPR:", " sproc | " );
-        recordString = recordString.replace( "PWM:", " sproc | " );
-
-        translatedString = auditRecord.getTimestamp().toString();
-        translatedString = translatedString.concat( " host CEF:0 | security | threatmanager | 1.0 | 100 " );
-        recordString = recordString.replace( ",", " " );
-
-        translatedString = translatedString.concat( recordString );
-        return ( translatedString );
+        return replacedValue;
     }
 
     @Getter
@@ -489,4 +558,15 @@ public class SyslogAuditService
             return newClass;
         }
     }
+
+    private Map<String, String> figureCefSyslogExtensions( final Configuration config )
+    {
+        final String pairSplitter = ":";
+        final String keyValueSplitter = ",";
+        final String configuredString = config.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_EXTENSIONS );
+
+        final List<String> pairs = Arrays.asList( configuredString.split( pairSplitter ) );
+        final Map<String, String> map = StringUtil.convertStringListToNameValuePair( pairs, keyValueSplitter );
+        return Collections.unmodifiableMap( map );
+    }
 }

+ 4 - 0
server/src/main/resources/password/pwm/AppProperty.properties

@@ -29,6 +29,10 @@ application.wordlistRetryImportSeconds=600
 audit.events.emailFrom=Audit Event Notification <@DefaultEmailFromAddress@>
 audit.events.emailSubject=@PwmAppName@ - Audit Event - %EVENT%
 audit.events.localdb.maxBulkRemovals=301
+audit.syslog.cef.extensions=type,cat:eventCode,act:timestamp,rt:message,msg:narrative,reason:perpetratorID,suid:perpetratorDN,suser:sourceAddress,dvc:sourceHost,dvchost:targetID,duid:targetDN,duser
+audit.syslog.cef.header.product=@PwmAppName@
+audit.syslog.cef.header.severity=Unknown
+audit.syslog.cef.header.vendor=@PwmAppName@
 audit.syslog.message.length=900
 audit.syslog.message.truncateMsg=[truncated]
 backup.path=backup

+ 4 - 0
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -1049,6 +1049,10 @@
         </default>
     </setting>
     <setting hidden="false" key="password.policy.maximumLength" level="1" required="true">
+        <properties>
+            <property key="Minimum">0</property>
+            <property key="Maximum">10000</property>
+        </properties>
         <default>
             <value>64</value>
         </default>

+ 1 - 1
server/src/main/resources/password/pwm/i18n/Config.properties

@@ -161,7 +161,7 @@ Tooltip_FormOptions_LinkLabel=Label to be displayed that tells where the link wi
 Tooltip_FormOptions_LinkURL=Full url that you want to go to when the link is selected.
 Tooltip_Form_ShowInNewWindow=Choose if the link will e opened in a new browser window
 Tooltip_FormOptions_Placeholder=Placeholder text to display in the form field with the field is not populated with a value.
-Tooltip_FormOptions_Javascript=Javascript to be added to the browser.
+Tooltip_FormOptions_Javascript=Javascript to be added to the browser.  This option is depreciated.  Use 'Settings -> User Interface -> Look & Feel -> Embedded JavaScript' instead.
 Tooltip_FormOptions_MultiValue=Display multiple values of the attribute.
 VerificationMethodDetail_PREVIOUS_AUTH=This method is passed when a user has previously authenticated using their browser.  There is no user interaction or display associated with this method.
 VerificationMethodDetail_ATTRIBUTES=User will be prompted for LDAP attribute values defined by the setting @PwmSettingReference:challenge.requiredAttributes@.

+ 1 - 0
server/src/main/resources/password/pwm/i18n/Health.properties

@@ -58,6 +58,7 @@ HealthMessage_Config_UserPermissionValidity=User Permission configuration for se
 HealthMessage_Config_DNValueValidity=LDAP DN configuration setting %1% issue: %2%.  This may cause unexpected issues.
 HealthMessage_Config_Certificate=Certificate for setting %1% issue: %2%
 HealthMessage_Config_InvalidSendMethod=The send method '%1%' is no longer available for setting %2%.  Please modify the configuration to use an alternate value.
+HealthMessage_Config_DeprecatedJSForm=The javascript form option in the form setting %1% has been deprecated and will be removed in a future version.  Use the setting %2% instead.
 HealthMessage_LDAP_VendorsNotSame=LDAP directories of different vendor types are in use.  This configuration may cause undesirable side effects and is not supported.  %1%
 HealthMessage_LDAP_Ad_History_Asn_Missing=%1% is enabled, but the server at %2% does not support this feature.  Check to be sure it is upgraded to Windows Server 2008 R2 SP1 or greater.  Password changes against this server may fail until this is resolved.
 HealthMessage_LDAP_RecentlyUnreachable=LDAP profile %1% was recently unavailable (%2% ago at %3%): %4%

+ 4 - 4
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -512,15 +512,15 @@ Setting_Description_password.policy.allowNumeric=Enable this option to allow num
 Setting_Description_password.policy.allowSpecial=Enable this option to allow special (non alpha-numeric) characters in the password.
 Setting_Description_password.policy.caseSensitivity=Enable this option to control if the password is case sensitive.  In most cases, @PwmAppName@ can read this from the directory, but in some cases, the system cannot correctly read this value, so you can override it here.
 Setting_Description_password.policy.changeMessage=Specify a message @PwmAppName@ displays to the users during password changes.  Might include HTML markup.  You can override this setting by adding a change password message read as part of an LDAP password policy.
-Setting_Description_password.policy.charGroup.minimumMatch=Specify tthe number of regular expression matches defined in the setting <code>@PwmSettingReference\:password.policy.charGroup.regExValues@</code>.
+Setting_Description_password.policy.charGroup.minimumMatch=Specify the number of regular expression matches defined in the setting <code>@PwmSettingReference\:password.policy.charGroup.regExValues@</code>.
 Setting_Description_password.policy.charGroup.regExValues=Add an LDAP filter that contains a list of regular expression character matches.  Along with the setting <code>@PwmSettingReference\:password.policy.charGroup.minimumMatch@</code>, this setting allows creating a complex list of requirements that the user only needs to partially match.  For example, you can use this type of policy to replicate the Active Directory "3 out of 5" rules, but with more flexibility and customization.
 Setting_Description_password.policy.checkWordlist=Enable this option to check the password against the configured Word List.
 Setting_Description_password.policy.disallowCurrent=Enable this option to prohibit the current password from being used as a new password.  Note: @PwmAppName@ can only enforce this if the login method permits the user's password to be known.
 Setting_Description_password.policy.disallowedAttributes=Specify a list of attributes not allowed to be used as passwords.  For a given user, @PwmAppName@ reads the values and does not permit the users to use it as part of the password value.  This check is case-insensitive.  Note: Specifying a number after the attribute name restricts how many consecutive characters @PwmAppName@ disallows in the value (For example: "Language:4" means the password cannot contain: "Engl", "ngli", "glis", or "lish", for English speaking users).
-Setting_Description_password.policy.disallowedValues=Speciy a case insensitive list of values @PwmAppName@ does not allow the users to use as passwords.
+Setting_Description_password.policy.disallowedValues=Specify a case insensitive list of values @PwmAppName@ does not allow the users to use as passwords.
 Setting_Description_password.policy.maximumAlpha=Specify the maximum amount of alphabetic characters required.  A value of zero disables this check.
-Setting_Description_password.policy.maximumConsecutive=Speicfy the maximum amount of characters in a sequence such as <b>0123456789</b> or <b>abcdefghijk</b>.  @PwmAppName@ defines a more specific character sequence by the unicode character order of each character after it converts the entire value to lowercase.   A value of 0 disables this check.
-Setting_Description_password.policy.maximumLength=Specify the maximum length of the password.  A value of zero disables this check.
+Setting_Description_password.policy.maximumConsecutive=Specify the maximum amount of characters in a sequence such as <b>0123456789</b> or <b>abcdefghijk</b>.  @PwmAppName@ defines a more specific character sequence by the unicode character order of each character after it converts the entire value to lowercase.   A value of 0 disables this check.
+Setting_Description_password.policy.maximumLength=Specify the maximum length of the password.  A value of zero disables this check.  Although you can set this limit to large values, the LDAP directory being used may have fixed limitations on the supported password length.
 Setting_Description_password.policy.maximumLowerCase=Specify the maximum amount of lowercase characters required.  A value of zero disables this check.
 Setting_Description_password.policy.maximumNonAlpha=Specify the maximum amount of non-alphabetic characters required.  A value of zero disables this check.
 Setting_Description_password.policy.maximumNumeric=Specify the maximum amount of numeric characters required (if the password policy allows numeric).  A value of zero disables this check.

+ 2 - 12
server/src/main/webapp/WEB-INF/jsp/accountinformation.jsp

@@ -42,7 +42,7 @@
     </jsp:include>
     <div id="centerbody">
         <div id="page-content-title" style="display: none;"><pwm:display key="Title_UserInformation" displayIfMissing="true"/></div>
-        <div class="tab-container" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false">
+        <div class="tab-container" style="width: 100%; height: 100%;">
             <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
             <label for="tab-1" class="label"><pwm:display key="Title_UserInformation"/></label>
             <div class="tab-content-pane" id="UserInformation" title="<pwm:display key="Title_UserInformation"/>" class="tabContent">
@@ -55,7 +55,7 @@
             </div>
             <% if (!JavaHelper.isEmpty(accountInformationBean.getFormData())) { %>
             <input name="tabs" type="radio" id="tab-2" class="input"/>
-            <label for="tab-2" class="label"><%=Display.Title_UserData.toString()%></label>
+            <label for="tab-2" class="label"><pwm:display key="Title_UserData"/></label>
             <div class="tab-content-pane" id="UserData" title="<pwm:display key="<%=Display.Title_UserData.toString()%>"/>" class="tabContent">
                 <div style="max-height: 400px; overflow: auto;">
                     <table class="nomargin">
@@ -126,16 +126,6 @@
     </div>
     <div class="push"></div>
 </div>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            require(["dojo/parser","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojoParser){
-                dojoParser.parse();
-            });
-        });
-    </script>
-</pwm:script>
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/tab-container.css' addContext="true"/>"/>
 <jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
 </body>
 </html>                   

+ 114 - 109
server/src/main/webapp/WEB-INF/jsp/admin-activity.jsp

@@ -49,20 +49,22 @@
     }
 </style>
 <div id="wrapper">
-    <% final String PageName = JspUtility.localizedString(pageContext,"Title_UserActivity",Admin.class);%>
+        <% final String PageName = JspUtility.localizedString(pageContext,"Title_UserActivity",Admin.class);%>
     <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp" flush="true" >
         <jsp:param name="pwm.PageName" value="<%=PageName%>"/>
     </jsp:include>
     <div id="centerbody" class="wide">
         <div id="page-content-title"><pwm:display key="Title_UserActivity" bundle="Admin"/></div>
         <%@ include file="fragment/admin-nav.jsp" %>
-        <div data-dojo-type="dijit/layout/TabContainer" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true">
-            <div id="ActiveWebSessions" data-dojo-type="dijit/layout/ContentPane" title="<pwm:display key="Title_Sessions" bundle="Admin"/>" class="tabContent">
+        <div id="ActivityTabContainer" class="tab-container" style="width: 100%; height: 100%;">
+            <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
+            <label for="tab-1" class="label"><pwm:display key="Title_Sessions" bundle="Admin"/></label>
+            <div id="SessionsTab" class="tab-content-pane" title="<pwm:display key="Title_Sessions" bundle="Admin"/>" >
+
                 <div id="activeSessionGrid" class="analysisGrid">
                 </div>
                 <div style="text-align: center">
-                    <input name="maxResults" id="maxActiveSessionResults" value="1000" data-dojo-type="dijit/form/NumberSpinner" style="width: 70px"
-                           data-dojo-props="constraints:{min:10,max:10000000,pattern:'#'},smallDelta:100"/>
+                    <input name="maxResults" id="maxActiveSessionResults" value="1000" type="number" min="10" max="10000000" style="width: 70px"/>
                     Rows
                     <button class="btn" type="button" id="button-activeSessionRefresh">
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
@@ -77,126 +79,129 @@
                             });
                         </script>
                     </pwm:script>
-
                 </div>
             </div>
+            <input name="tabs" type="radio" id="tab-2" class="input"/>
+            <label for="tab-2" class="label"><pwm:display key="Title_Intruders" bundle="Admin"/></label>
+            <div id="IntrudersTab" class="tab-content-pane" title="<pwm:display key="Title_Intruders" bundle="Admin"/>">
+                <div class="tab-container" style="width: 100%; height: 100%;">
+                    <% boolean checked = true; %>
+                    <% for (final RecordType recordType : RecordType.values()) { %>
+                    <% final String titleName = LocaleHelper.getLocalizedMessage(activity_pwmRequest.getLocale(),"IntruderRecordType_" + recordType.toString(), activity_pwmRequest.getConfig(), Admin.class); %>
+                    <input name="intruder_tabs" type="radio" id="tab-2.<%=recordType%>" <%=checked?"checked=\"checked\"":""%> class="input"/>
+                    <label for="tab-2.<%=recordType%>" class="label"><%=titleName%></label>
+                    <div class="tab-content-pane" title="<%=titleName%>">
 
-
-            <div data-dojo-type="dijit/layout/TabContainer" title="<pwm:display key="Title_Intruders" bundle="Admin"/>" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true">
-            <% for (final RecordType recordType : RecordType.values()) { %>
-            <% final String titleName = LocaleHelper.getLocalizedMessage(activity_pwmRequest.getLocale(),"IntruderRecordType_" + recordType.toString(), activity_pwmRequest.getConfig(), Admin.class); %>
-            <div id="Intruders<%=titleName%>" data-dojo-type="dijit/layout/ContentPane" title="<%=titleName%>" class="tabContent">
-                <div id="<%=recordType%>_Grid" class="analysisGrid">
+                            <div id="<%=recordType%>_Grid" class="analysisGrid">
+                            </div>
+                    </div>
+                    <% checked = false; %>
+                    <% } %>
+                    <div class="tab-end"></div>
                 </div>
             </div>
-            <% } %>
-            </div>
+            <input name="tabs" type="radio" id="tab-3" class="input"/>
+            <label for="tab-3" class="label" id="audit_tab_label"><pwm:display key="Title_Audit" bundle="Admin"/></label>
+            <div id="AuditTab" class="tab-content-pane" title="<pwm:display key="Title_Audit" bundle="Admin"/>">
+                <div class="tab-container" style="width: 100%; height: 100%;">
 
-            <div data-dojo-type="dijit/layout/TabContainer" title="<pwm:display key="Title_Audit" bundle="Admin"/>" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true">
-            <div id="AuditUser" data-dojo-type="dijit/layout/ContentPane" title="<pwm:display key="Title_AuditUsers" bundle="Admin"/>" class="tabContent">
-                <div id="auditUserGrid" class="analysisGrid">
-                </div>
-                <div style="text-align: center">
-                    <input name="maxAuditUserResults" id="maxAuditUserResults" value="1000" data-dojo-type="dijit/form/NumberSpinner" style="width: 70px"
-                           data-dojo-props="constraints:{min:10,max:10000000,pattern:'#'},smallDelta:100"/>
-                    Rows
-                    <button class="btn" type="button" id="button-refreshAuditUser">
-                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
-                        <pwm:display key="Button_Refresh" bundle="Admin"/>
-                    </button>
-                    <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
-                        <button type="submit" class="btn">
-                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
-                            <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
-                        </button>
-                        <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
-                        <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
-                    </form>
-                </div>
-            </div>
-            <div id="AuditHelpdesk" data-dojo-type="dijit/layout/ContentPane" title="<pwm:display key="Title_AuditHelpdesk" bundle="Admin"/>" class="tabContent">
-                <div id="auditHelpdeskGrid" class="analysisGrid">
-                </div>
-                <div style="text-align: center">
-                    <input name="maxAuditHelpdeskResults" id="maxAuditHelpdeskResults" value="1000" data-dojo-type="dijit/form/NumberSpinner" style="width: 70px"
-                           data-dojo-props="constraints:{min:10,max:10000000,pattern:'#'},smallDelta:100"/>
-                    Rows
-                    <button class="btn" type="button" id="button-refreshHelpdeskUser">
-                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
-                        <pwm:display key="Button_Refresh" bundle="Admin"/>
-                    </button>
-                    <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
-                        <button type="submit" class="btn">
-                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
-                            <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
-                        </button>
-                        <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
-                        <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
-                    </form>
-                </div>
-            </div>
-            <div id="AuditSystem" data-dojo-type="dijit/layout/ContentPane" title="<pwm:display key="Title_AuditSystem" bundle="Admin"/>" class="tabContent">
-                <div id="auditSystemGrid" class="analysisGrid">
-                </div>
-                <div style="text-align: center">
-                    <input name="maxAuditSystemResults" id="maxAuditSystemResults" value="1000" data-dojo-type="dijit/form/NumberSpinner" style="width: 70px"
-                           data-dojo-props="constraints:{min:10,max:10000000,pattern:'#'},smallDelta:100"/>
-                    Rows
-                    <button class="btn" type="button" id="button-refreshSystemAudit">
-                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
-                        <pwm:display key="Button_Refresh" bundle="Admin"/>
-                    </button>
-                    <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
-                        <button type="submit" class="btn">
-                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
-                            <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
-                        </button>
-                        <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
-                        <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
-                    </form>
+                    <input name="audit_tabs" type="radio" id="tab-3.1" checked="checked" class="input"/>
+                    <label for="tab-3.1" class="label"><pwm:display key="Title_AuditUsers" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_AuditUsers" bundle="Admin"/>" class="tabContent">
+
+                        <div id="auditUserGrid" class="analysisGrid">
+                        </div>
+
+                        <div style="text-align: center">
+                            <input name="maxAuditUserResults" id="maxAuditUserResults" value="100" type="number" min="10" max="10000000" style="width: 70px"/>
+                            Rows
+                            <button class="btn" type="button" id="button-refreshAuditUser">
+                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
+                                <pwm:display key="Button_Refresh" bundle="Admin"/>
+                            </button>
+                            <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
+                                <button type="submit" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                            </form>
+                        </div>
+                    </div>
+                    <input name="audit_tabs" type="radio" id="tab-3.2" class="input"/>
+                    <label for="tab-3.2" class="label"><pwm:display key="Title_AuditHelpdesk" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_AuditHelpdesk" bundle="Admin"/>" class="tabContent">
+                        <div id="auditHelpdeskGrid" class="analysisGrid">
+                        </div>
+                        <div style="text-align: center">
+                            <input name="maxAuditHelpdeskResults" id="maxAuditHelpdeskResults" value="100" type="number" min="10" max="10000000" style="width: 70px"/>
+                            Rows
+                            <button class="btn" type="button" id="button-refreshHelpdeskUser">
+                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
+                                <pwm:display key="Button_Refresh" bundle="Admin"/>
+                            </button>
+                            <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
+                                <button type="submit" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                            </form>
+                        </div>
+                    </div>
+                    <input name="audit_tabs" type="radio" id="tab-3.3" class="input"/>
+                    <label for="tab-3.3" class="label"><pwm:display key="Title_AuditSystem" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_AuditSystem" bundle="Admin"/>" class="tabContent">
+                        <div id="auditSystemGrid" class="analysisGrid">
+                        </div>
+                        <div style="text-align: center">
+                            <input name="maxAuditSystemResults" id="maxAuditSystemResults" value="100" type="number" min="10" max="10000000" style="width: 70px"/>
+                            Rows
+                            <button class="btn" type="button" id="button-refreshSystemAudit">
+                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
+                                <pwm:display key="Button_Refresh" bundle="Admin"/>
+                            </button>
+                            <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
+                                <button type="submit" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                            </form>
+                        </div>
+                    </div>
+                    <div class="tab-end"></div>
                 </div>
             </div>
-            </div>
-        </div>
-        <br/>
-        <%--
-        <div style="text-align: center">
-            <input name="maxResults" id="maxIntruderGridResults" value="1000" data-dojo-type="dijit/form/NumberSpinner" style="width: 70px"
-                   data-dojo-props="constraints:{min:10,max:10000000,pattern:'#'},smallDelta:100"/>
-            Rows
-            <button class="btn" type="button" onclick="PWM_ADMIN.refreshIntruderGrid()">
-                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
-                <pwm:display key="Button_Refresh" bundle="Admin"/>
-            </button>
+            <div class="tab-end"></div>
         </div>
-        --%>
+        <div class="push"></div>
     </div>
-    <div class="push"></div>
-</div>
-<pwm:script>
+    <pwm:script>
     <script type="text/javascript">
         PWM_GLOBAL['startupFunctions'].push(function(){
-            require(["dojo/parser","dojo/ready","dijit/layout/TabContainer","dijit/layout/ContentPane","dijit/Dialog","dijit/form/NumberSpinner"],function(dojoParser,ready){
-                dojoParser.parse(PWM_MAIN.getObject('centerbody'));
-                PWM_ADMIN.initIntrudersGrid();
-                PWM_ADMIN.initActiveSessionGrid();
-                PWM_ADMIN.initAuditGrid();
-
+            PWM_MAIN.addEventHandler('audit_tab_label','click',function(){
+            });
 
+            PWM_ADMIN.initAuditGrid();
+            PWM_ADMIN.initActiveSessionGrid();
+            PWM_ADMIN.initIntrudersGrid();
 
-                PWM_MAIN.addEventHandler('button-refreshAuditUser','click',function(){
-                    PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditUserResults').value);
-                });
-                PWM_MAIN.addEventHandler('button-refreshHelpdeskUser','click',function(){
-                    PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditHelpdeskResults').value);
-                });
-                PWM_MAIN.addEventHandler('button-refreshSystemAudit','click',function(){
-                    PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditSystemResults').value);
-                });
+            PWM_MAIN.addEventHandler('button-refreshAuditUser','click',function(){
+                PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditUserResults').value,'USER');
+            });
+            PWM_MAIN.addEventHandler('button-refreshHelpdeskUser','click',function(){
+                PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditHelpdeskResults').value,'HELPDESK');
+            });
+            PWM_MAIN.addEventHandler('button-refreshSystemAudit','click',function(){
+                PWM_ADMIN.refreshAuditGridData(PWM_MAIN.getObject('maxAuditSystemResults').value,'SYSTEM');
             });
         });
     </script>
-</pwm:script>
-<%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
+    </pwm:script>
+    <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 5 - 8
server/src/main/webapp/WEB-INF/jsp/admin-analysis.jsp

@@ -63,10 +63,10 @@
     <div id="centerbody" class="wide">
         <div id="page-content-title"><pwm:display key="Title_DataAnalysis" bundle="Admin"/></div>
         <%@ include file="fragment/admin-nav.jsp" %>
-        <div class="tab-container" style="width: 100%; height: 100%;"  data-dojo-props="doLayout: false, persist: true" id="analysis-topLevelTab">
+        <div class="tab-container" style="width: 100%; height: 100%;" id="analysis-topLevelTab">
             <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
             <label for="tab-1" class="label"><pwm:display key="Title_DirectoryReporting" bundle="Admin"/></label>
-            <div class="tab-content-pane" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true" title="<pwm:display key="Title_DirectoryReporting" bundle="Admin"/>">
+            <div class="tab-content-pane" style="width: 100%; height: 100%;" title="<pwm:display key="Title_DirectoryReporting" bundle="Admin"/>">
                 <div class="tab-container" style="width: 100%; height: 100%;">
                     <input name="dr_tabs" type="radio" id="tab-1.1" checked="checked" class="input"/>
                     <label for="tab-1.1" class="label"><pwm:display key="Title_ReportEngineStatus" bundle="Admin"/></label>
@@ -169,7 +169,7 @@
 
             <input name="tabs" type="radio" id="tab-2" class="input"/>
             <label for="tab-2" class="label"><pwm:display key="Title_EventStatistics" bundle="Admin"/></label>
-            <div class="tab-content-pane" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true" title="<pwm:display key="Title_EventStatistics" bundle="Admin"/>">
+            <div class="tab-content-pane" style="width: 100%; height: 100%;" title="<pwm:display key="Title_EventStatistics" bundle="Admin"/>">
                 <div class="tab-container" style="width: 100%; height: 100%;">
                     <input name="es_tabs" type="radio" id="tab-2.1" checked="checked" class="input"/>
                     <label for="tab-2.1" class="label"><pwm:display key="Title_RawStatistics" bundle="Admin"/></label>
@@ -181,7 +181,7 @@
                                         <form action="<pwm:current-url/>" method="GET" enctype="application/x-www-form-urlencoded"
                                               name="statsUpdateForm" id="statsUpdateForm">
                                             <select name="statsPeriodSelect"
-                                                    style="width: 350px;" data-dojo-props="maxHeight: -1">
+                                                    style="width: 350px;">
                                                 <option value="<%=StatisticsManager.KEY_CUMULATIVE%>" <%= StatisticsManager.KEY_CUMULATIVE.equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
                                                     since installation - <%= JavaHelper.toIsoDate(analysis_pwmRequest.getPwmApplication().getInstallTime()) %>
                                                 </option>
@@ -253,7 +253,6 @@
                 </div>
             </div>
 
-            <div class="tab-end"></div>
         </div>
     </div>
     <div class="push"></div>
@@ -269,8 +268,7 @@
 
 
         PWM_GLOBAL['startupFunctions'].push(function(){
-            require(["dojo","dojo/query","dojo/parser","dijit/registry","dojo/ready","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojo,query,dojoParser,registry,ready){
-                dojoParser.parse('centerbody');
+            require(["dojo","dojo/query"],function(dojo,query){
                 PWM_MAIN.JSLibrary.setValueOfSelectElement('statsChartSelect','<%=Statistic.PASSWORD_CHANGES%>');
 
                 setTimeout(function(){
@@ -308,7 +306,6 @@
     </script>
 </pwm:script>
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.HIDE_LOCALE); %>
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/tab-container.css' addContext="true"/>"/>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 37 - 39
server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp

@@ -55,7 +55,7 @@
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_Dashboard" bundle="Admin"/></div>
         <%@ include file="fragment/admin-nav.jsp" %>
-        <div id="DashboardTabContainer" class="tab-container" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false, persist: true">
+        <div id="DashboardTabContainer" class="tab-container" style="width: 100%; height: 100%;">
             <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
             <label for="tab-1" class="label">Status</label>
             <div id="StatusTab" class="tab-content-pane" title="Status" >
@@ -109,7 +109,7 @@
                     <% } %>
                     <% } %>
                 </table>
-                <div class="tab-container" style="margin-top: 15px;" data-dojo-props="doLayout: false, persist: true">
+                <div class="tab-container" style="margin-top: 15px;">
                     <input name="status_tabs" type="radio" id="tab-1.1" checked="checked" class="input"/>
                     <label for="tab-1.1" class="label">Last Minute</label>
                     <div class="tab-content-pane" title="Last Minute" class="tabContent">
@@ -242,6 +242,7 @@
             <input name="tabs" type="radio" id="tab-4" class="input"/>
             <label for="tab-4" class="label">Services</label>
             <div id="ServicesTab" class="tab-content-pane" title="Services">
+                <div style="max-height: 600px; overflow: auto;">
                 <table class="nomargin">
                     <tr>
                         <th style="font-weight:bold;">
@@ -289,6 +290,7 @@
                     </tr>
                     <% } %>
                 </table>
+                </div>
             </div>
 
             <input name="tabs" type="radio" id="tab-5" class="input"/>
@@ -304,32 +306,32 @@
                 </div>
                 <br/>
                 <div style="max-height: 400px; overflow: auto;">
-                <% if (!JavaHelper.isEmpty(appDashboardData.getLocalDbSizes())) { %>
-                <table class="nomargin">
-                    <tr>
-                        <td class="key">
-                            Name
-                        </td>
-                        <td class="key" style="text-align: left">
-                            Record Count
-                        </td>
-                    </tr>
-                    <% for (final Map.Entry<LocalDB.DB,String> entry : appDashboardData.getLocalDbSizes().entrySet()) { %>
-                    <tr>
-                        <td style="text-align: right">
-                            <%= entry.getKey() %>
-                        </td>
-                        <td>
-                            <%= entry.getValue() %>
-                        </td>
-                    </tr>
+                    <% if (!JavaHelper.isEmpty(appDashboardData.getLocalDbSizes())) { %>
+                    <table class="nomargin">
+                        <tr>
+                            <td class="key">
+                                Name
+                            </td>
+                            <td class="key" style="text-align: left">
+                                Record Count
+                            </td>
+                        </tr>
+                        <% for (final Map.Entry<LocalDB.DB,String> entry : appDashboardData.getLocalDbSizes().entrySet()) { %>
+                        <tr>
+                            <td style="text-align: right">
+                                <%= entry.getKey() %>
+                            </td>
+                            <td>
+                                <%= entry.getValue() %>
+                            </td>
+                        </tr>
+                        <% } %>
+                    </table>
+                    <% } else { %>
+                    <div class="noborder" style="text-align:center; width:100%;">
+                        <a style="cursor: pointer" id="button-showLocalDBCounts">Show LocalDB record counts</a> (may be slow to load)
+                    </div>
                     <% } %>
-                </table>
-                <% } else { %>
-                <div class="noborder" style="text-align:center; width:100%;">
-                    <a style="cursor: pointer" id="button-showLocalDBCounts">Show LocalDB record counts</a> (may be slow to load)
-                </div>
-                <% } %>
                 </div>
             </div>
 
@@ -449,21 +451,18 @@
 <pwm:script>
     <script type="text/javascript">
         PWM_GLOBAL['startupFunctions'].push(function(){
-            require(["dojo/parser","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojoParser){
-                dojoParser.parse();
-                PWM_ADMIN.showStatChart('PASSWORD_CHANGES',14,'statsChart',{refreshTime:11*1000});
-                PWM_ADMIN.showAppHealth('healthBody', {showRefresh:true,showTimestamp:true});
+            PWM_ADMIN.showStatChart('PASSWORD_CHANGES',14,'statsChart',{refreshTime:11*1000});
+            PWM_ADMIN.showAppHealth('healthBody', {showRefresh:true,showTimestamp:true});
 
-                PWM_MAIN.addEventHandler('button-showLocalDBCounts','click',function(){
-                    PWM_MAIN.showWaitDialog({loadFunction:function(){
+            PWM_MAIN.addEventHandler('button-showLocalDBCounts','click',function(){
+                PWM_MAIN.showWaitDialog({loadFunction:function(){
                         PWM_MAIN.goto('dashboard?showLocalDBCounts=true');
                     }})
-                });
-                PWM_MAIN.addEventHandler('button-showThreadDetails','click',function(){
-                    PWM_MAIN.showWaitDialog({loadFunction:function(){
+            });
+            PWM_MAIN.addEventHandler('button-showThreadDetails','click',function(){
+                PWM_MAIN.showWaitDialog({loadFunction:function(){
                         PWM_MAIN.goto('dashboard?showThreadDetails=true');
                     }})
-                });
             });
             <% for (final AppDashboardData.ServiceData loopService : appDashboardData.getServices()) { %>
             <% if (!JavaHelper.isEmpty(loopService.getDebugData())) { %>
@@ -471,7 +470,7 @@
                 var tableText = '<table>';
                 <% for (final Map.Entry<String,String> entry : loopService.getDebugData().entrySet()) { %>
                 tableText += '<tr><td><%=StringUtil.escapeJS(entry.getKey())%></td>'
-                + '<td><%=StringUtil.escapeJS(entry.getValue())%></td></tr>';
+                    + '<td><%=StringUtil.escapeJS(entry.getValue())%></td></tr>';
                 <% } %>
                 tableText += '</table>';
                 PWM_MAIN.showDialog({title:'Debug Properties',text:tableText});
@@ -482,7 +481,6 @@
     </script>
 </pwm:script>
 
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/tab-container.css' addContext="true"/>"/>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 </body>

+ 2 - 2
server/src/main/webapp/WEB-INF/jsp/fragment/displayelement-row.jsp

@@ -29,11 +29,11 @@
         <%= displayElement.getLabel() %>
     </td>
     <% if (displayElement.getType() == DisplayElement.Type.timestamp) { %>
-    <td class="timestamp">
+    <td class="timestamp" id="<%=StringUtil.escapeHtml(displayElement.getKey())%>">
         <%= StringUtil.escapeHtml(displayElement.getValue()) %>
     </td>
     <% } else { %>
-    <td>
+    <td  id="<%=StringUtil.escapeHtml(displayElement.getKey())%>">
         <%= StringUtil.escapeHtml(displayElement.getValue()) %>
     </td>
     <% } %>

+ 9 - 0
server/src/main/webapp/WEB-INF/jsp/fragment/footer.jsp

@@ -25,6 +25,15 @@
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.PwmRequestFlag" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
+
+<pwm:if test="<%=PwmIfTest.requestFlag%>" requestFlag="<%=PwmRequestFlag.INCLUDE_IAS_ANGULAR%>">
+    <pwm:script-ref url="/public/resources/webjars/angular/angular.min.js" />
+    <pwm:script-ref url="/public/resources/webjars/angular-aria/angular-aria.min.js" />
+    <pwm:script-ref url="/public/resources/webjars/angular-ui-router/release/angular-ui-router.min.js" />
+    <pwm:script-ref url="/public/resources/webjars/angular-translate/dist/angular-translate.min.js" />
+    <pwm:script-ref url="/public/resources/webjars/pwm-client/vendor/ng-ias.js" />
+</pwm:if>
+
 <%-- begin pwm footer --%>
 <pwm:if test="<%=PwmIfTest.requestFlag%>" requestFlag="<%=PwmRequestFlag.HIDE_FOOTER_TEXT%>" negate="true">
     <div id="footer">

+ 4 - 0
server/src/main/webapp/WEB-INF/jsp/fragment/header.jsp

@@ -64,4 +64,8 @@
             var PWM_GLOBAL = PWM_GLOBAL || {}; PWM_GLOBAL['startupFunctions'] = [];
         </script>
     </pwm:script>
+    <pwm:if test="<%=PwmIfTest.requestFlag%>" requestFlag="<%=PwmRequestFlag.INCLUDE_IAS_ANGULAR%>">
+        <link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/webjars/pwm-client/vendor/ias-icons.css' addContext="true"/>"/>
+        <link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/webjars/pwm-client/vendor/ux-ias.css' addContext="true"/>"/>
+    </pwm:if>
 </head>

+ 2 - 5
server/src/main/webapp/WEB-INF/jsp/helpdesk.jsp

@@ -24,6 +24,7 @@
 <%@ page import="password.pwm.http.PwmRequestAttribute" %>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequestFlag.INCLUDE_IAS_ANGULAR); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
@@ -44,13 +45,9 @@
     <div class="push"></div>
 </div>
 
-<pwm:script-ref url="/public/resources/webjars/angular/angular.min.js" />
-<pwm:script-ref url="/public/resources/webjars/angular-ui-router/release/angular-ui-router.min.js" />
-<pwm:script-ref url="/public/resources/webjars/angular-translate/dist/angular-translate.min.js" />
-
 <jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
+
 <pwm:script-ref url="/public/resources/js/helpdesk.js"/>
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/webjars/pwm-client/fonts.css' addContext="true"/>"/>
 <pwm:script-ref url="/public/resources/webjars/pwm-client/helpdesk.ng.js" />
 <pwm:script-ref url="/public/resources/js/changepassword.js"/>
 

+ 2 - 5
server/src/main/webapp/WEB-INF/jsp/peoplesearch.jsp

@@ -21,6 +21,7 @@
 --%>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequestFlag.INCLUDE_IAS_ANGULAR); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
@@ -38,12 +39,8 @@
     <div class="push"></div>
 </div>
 
-<pwm:script-ref url="/public/resources/webjars/angular/angular.min.js" />
-<pwm:script-ref url="/public/resources/webjars/angular-ui-router/release/angular-ui-router.min.js" />
-<pwm:script-ref url="/public/resources/webjars/angular-translate/dist/angular-translate.min.js" />
-
 <%@ include file="fragment/footer.jsp" %>
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/webjars/pwm-client/fonts.css' addContext="true"/>"/>
+
 <pwm:script-ref url="/public/resources/webjars/pwm-client/peoplesearch.ng.js" />
 
 </body>

+ 1 - 13
server/src/main/webapp/WEB-INF/jsp/setupotpsecret.jsp

@@ -55,7 +55,7 @@
         <div id="page-content-title"><pwm:display key="Title_SetupOtpSecret" displayIfMissing="true"/></div>
         <p><pwm:display key="Display_SetupOtpSecret"/></p>
         <%@ include file="fragment/message.jsp" %>
-        <div class="tab-container" data-dojo-props="doLayout: false, persist: true">
+        <div class="tab-container">
             <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
             <label for="tab-1" class="label"><pwm:display key="Display_SetupOtp_Android_Title"/></label>
             <div class="tab-content-pane" title="<pwm:display key="Display_SetupOtp_Android_Title"/>">
@@ -126,20 +126,8 @@
     </div>
     <div class="push"></div>
 </div>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            require(["dojo/parser","dojo/ready","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojoParser,ready){
-                ready(function(){
-                    dojoParser.parse();
-                });
-            });
-        });
-    </script>
-</pwm:script>
 <pwm:script-ref url="/public/resources/js/responses.js"/>
 <pwm:script-ref url="/public/resources/js/otpsecret.js"/>
-<link rel="stylesheet" type="text/css" href="<pwm:url url='/public/resources/tab-container.css' addContext="true"/>"/>
 <%@ include file="fragment/footer.jsp" %>
 </body>
 </html>

+ 131 - 158
server/src/main/webapp/public/resources/js/admin.js

@@ -114,30 +114,30 @@ PWM_ADMIN.initAdminNavMenu = function() {
 };
 
 PWM_ADMIN.reportDataHeaders = function() {
-    return {
-        "username":PWM_ADMIN.showString("Field_Report_Username"),
-        "userDN":PWM_ADMIN.showString("Field_Report_UserDN"),
-        "ldapProfile":PWM_ADMIN.showString("Field_Report_LDAP_Profile"),
-        "email":PWM_ADMIN.showString("Field_Report_Email"),
-        "userGUID":PWM_ADMIN.showString("Field_Report_UserGuid"),
-        "accountExpirationTime":PWM_ADMIN.showString("Field_Report_AccountExpireTime"),
-        "passwordExpirationTime":PWM_ADMIN.showString("Field_Report_PwdExpireTime"),
-        "passwordChangeTime":PWM_ADMIN.showString("Field_Report_PwdChangeTime"),
-        "responseSetTime":PWM_ADMIN.showString("Field_Report_ResponseSaveTime"),
-        "lastLoginTime":PWM_ADMIN.showString("Field_Report_LastLogin"),
-        "hasResponses":PWM_ADMIN.showString("Field_Report_HasResponses"),
-        "hasHelpdeskResponses":PWM_ADMIN.showString("Field_Report_HasHelpdeskResponses"),
-        "responseStorageMethod":PWM_ADMIN.showString("Field_Report_ResponseStorageMethod"),
-        "responseFormatType":PWM_ADMIN.showString("Field_Report_ResponseFormatType"),
-        "passwordStatusExpired":PWM_ADMIN.showString("Field_Report_PwdExpired"),
-        "passwordStatusPreExpired":PWM_ADMIN.showString("Field_Report_PwdPreExpired"),
-        "passwordStatusViolatesPolicy":PWM_ADMIN.showString("Field_Report_PwdViolatesPolicy"),
-        "passwordStatusWarnPeriod":PWM_ADMIN.showString("Field_Report_PwdWarnPeriod"),
-        "requiresPasswordUpdate":PWM_ADMIN.showString("Field_Report_RequiresPasswordUpdate"),
-        "requiresResponseUpdate":PWM_ADMIN.showString("Field_Report_RequiresResponseUpdate"),
-        "requiresProfileUpdate":PWM_ADMIN.showString("Field_Report_RequiresProfileUpdate"),
-        "cacheTimestamp":PWM_ADMIN.showString("Field_Report_RecordCacheTime")
-    };
+    return [
+        {field:"username",label:PWM_ADMIN.showString("Field_Report_Username")},
+        {field:"userDN",label:PWM_ADMIN.showString("Field_Report_UserDN"),hidden:true},
+        {field:"ldapProfile",label:PWM_ADMIN.showString("Field_Report_LDAP_Profile"),hidden:true},
+        {field:"email",label:PWM_ADMIN.showString("Field_Report_Email"),hidden:true},
+        {field:"userGUID",label:PWM_ADMIN.showString("Field_Report_UserGuid"),hidden:true},
+        {field:"accountExpirationTime",label:PWM_ADMIN.showString("Field_Report_AccountExpireTime")},
+        {field:"passwordExpirationTime",label:PWM_ADMIN.showString("Field_Report_PwdExpireTime")},
+        {field:"passwordChangeTime",label:PWM_ADMIN.showString("Field_Report_PwdChangeTime")},
+        {field:"responseSetTime",label:PWM_ADMIN.showString("Field_Report_ResponseSaveTime")},
+        {field:"lastLoginTime",label:PWM_ADMIN.showString("Field_Report_LastLogin")},
+        {field:"hasResponses",label:PWM_ADMIN.showString("Field_Report_HasResponses")},
+        {field:"hasHelpdeskResponses",label:PWM_ADMIN.showString("Field_Report_HasHelpdeskResponses"),hidden:true},
+        {field:"responseStorageMethod",label:PWM_ADMIN.showString("Field_Report_ResponseStorageMethod"),hidden:true},
+        {field:"responseFormatType",label:PWM_ADMIN.showString("Field_Report_ResponseFormatType"),hidden:true},
+        {field:"passwordStatusExpired",label:PWM_ADMIN.showString("Field_Report_PwdExpired"),hidden:true},
+        {field:"passwordStatusPreExpired",label:PWM_ADMIN.showString("Field_Report_PwdPreExpired"),hidden:true},
+        {field:"passwordStatusViolatesPolicy",label:PWM_ADMIN.showString("Field_Report_PwdViolatesPolicy"),hidden:true},
+        {field:"passwordStatusWarnPeriod",label:PWM_ADMIN.showString("Field_Report_PwdWarnPeriod"),hidden:true},
+        {field:"requiresPasswordUpdate",label:PWM_ADMIN.showString("Field_Report_RequiresPasswordUpdate")},
+        {field:"requiresResponseUpdate",label:PWM_ADMIN.showString("Field_Report_RequiresResponseUpdate")},
+        {field:"requiresProfileUpdate",label:PWM_ADMIN.showString("Field_Report_RequiresProfileUpdate")},
+        {field:"cacheTimestamp",label:PWM_ADMIN.showString("Field_Report_RecordCacheTime"),hidden:true}
+    ];
 };
 
 PWM_ADMIN.initReportDataGrid=function() {
@@ -153,20 +153,6 @@ PWM_ADMIN.initReportDataGrid=function() {
             // Now, create an instance of our custom grid
             PWM_VAR['reportGrid'] = new CustomGrid({columns: columnHeaders}, "grid");
 
-            // unclick superfluous fields
-            PWM_MAIN.getObject('grid-hider-menu-check-cacheTimestamp').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-ldapProfile').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-email').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-userGUID').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-responseStorageMethod').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-responseFormatType').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-userDN').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-hasHelpdeskResponses').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-passwordStatusExpired').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-passwordStatusPreExpired').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-passwordStatusViolatesPolicy').click();
-            PWM_MAIN.getObject('grid-hider-menu-check-passwordStatusWarnPeriod').click();
-
             PWM_VAR['reportGrid'].on(".dgrid-row:click", function(evt){
                 PWM_ADMIN.detailView(evt, PWM_ADMIN.reportDataHeaders(), PWM_VAR['reportGrid']);
             });
@@ -280,36 +266,36 @@ PWM_ADMIN.reportAction=function(action) {
     confirmText = PWM_ADMIN.showString('Confirm_Report_' + action);
     actionText = PWM_ADMIN.showString('Display_Report_Action_' + action);
     PWM_MAIN.showConfirmDialog({text:confirmText,okAction:function(){
-        PWM_MAIN.showWaitDialog({title:PWM_MAIN.showString('Display_PleaseWait'),text:actionText,loadFunction:function(){
-            var url = PWM_GLOBAL['url-context'] + "/private/admin";
-            url = PWM_MAIN.addParamToUrl(url, 'processAction','reportCommand');
-            url = PWM_MAIN.addParamToUrl(url, 'command',action);
-            PWM_MAIN.ajaxRequest(url,function(){
-                setTimeout(function(){
-                    PWM_ADMIN.refreshReportDataStatus();
-                    PWM_ADMIN.refreshReportDataSummary();
-                    PWM_MAIN.closeWaitDialog();
-                },7500);
-            });
+            PWM_MAIN.showWaitDialog({title:PWM_MAIN.showString('Display_PleaseWait'),text:actionText,loadFunction:function(){
+                    var url = PWM_GLOBAL['url-context'] + "/private/admin";
+                    url = PWM_MAIN.addParamToUrl(url, 'processAction','reportCommand');
+                    url = PWM_MAIN.addParamToUrl(url, 'command',action);
+                    PWM_MAIN.ajaxRequest(url,function(){
+                        setTimeout(function(){
+                            PWM_ADMIN.refreshReportDataStatus();
+                            PWM_ADMIN.refreshReportDataSummary();
+                            PWM_MAIN.closeWaitDialog();
+                        },7500);
+                    });
+                }});
         }});
-    }});
 };
 
 PWM_ADMIN.webSessionHeaders = function() {
-    return {
-        "userID":PWM_ADMIN.showString('Field_Session_UserID'),
-        "ldapProfile":PWM_ADMIN.showString('Field_Session_LdapProfile'),
-        "userDN":PWM_ADMIN.showString('Field_Session_UserDN'),
-        "createTime":PWM_ADMIN.showString('Field_Session_CreateTime'),
-        "lastTime":PWM_ADMIN.showString('Field_Session_LastTime'),
-        "label":PWM_ADMIN.showString('Field_Session_Label'),
-        "idle":PWM_ADMIN.showString('Field_Session_Idle'),
-        "locale":PWM_ADMIN.showString('Field_Session_Locale'),
-        "srcAddress":PWM_ADMIN.showString('Field_Session_SrcAddress'),
-        "srcHost":PWM_ADMIN.showString('Field_Session_SrcHost'),
-        "lastUrl":PWM_ADMIN.showString('Field_Session_LastURL'),
-        "intruderAttempts":PWM_ADMIN.showString('Field_Session_IntruderAttempts')
-    };
+    return [
+        {field:"userID",label:PWM_ADMIN.showString('Field_Session_UserID')},
+        {field:"ldapProfile",label:PWM_ADMIN.showString('Field_Session_LdapProfile')},
+        {field:"userDN",label:PWM_ADMIN.showString('Field_Session_UserDN'),hidden:true},
+        {field:"createTime",label:PWM_ADMIN.showString('Field_Session_CreateTime')},
+        {field:"lastTime",label:PWM_ADMIN.showString('Field_Session_LastTime')},
+        {field:"label",label:PWM_ADMIN.showString('Field_Session_Label')},
+        {field:"idle",label:PWM_ADMIN.showString('Field_Session_Idle')},
+        {field:"locale",label:PWM_ADMIN.showString('Field_Session_Locale'),hidden:true},
+        {field:"srcAddress",label:PWM_ADMIN.showString('Field_Session_SrcAddress')},
+        {field:"srcHost",label:PWM_ADMIN.showString('Field_Session_SrcHost'),hidden:true},
+        {field:"lastUrl",label:PWM_ADMIN.showString('Field_Session_LastURL'),hidden:true},
+        {field:"intruderAttempts",label:PWM_ADMIN.showString('Field_Session_IntruderAttempts'),hidden:true}
+    ];
 };
 
 PWM_ADMIN.initActiveSessionGrid=function() {
@@ -327,14 +313,6 @@ PWM_ADMIN.initActiveSessionGrid=function() {
                 columns: columnHeaders
             }, "activeSessionGrid");
 
-            // unclick superfluous fields
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-label').click();
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-userDN').click();
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-srcHost').click();
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-locale').click();
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-lastUrl').click();
-            PWM_MAIN.getObject('activeSessionGrid-hider-menu-check-intruderAttempts').click();
-
             PWM_ADMIN.refreshActiveSessionGrid();
 
             PWM_VAR['activeSessionsGrid'].on(".dgrid-row:click", function(evt){
@@ -358,12 +336,12 @@ PWM_ADMIN.refreshActiveSessionGrid=function() {
 };
 
 PWM_ADMIN.intruderHeaders = function(){
-    return {
-        "subject":PWM_ADMIN.showString('Field_Intruder_Subject'),
-        "timestamp":PWM_ADMIN.showString('Field_Intruder_Timestamp'),
-        "count":PWM_ADMIN.showString('Field_Intruder_Count'),
-        "status":PWM_ADMIN.showString('Field_Intruder_Status')
-    };
+    return [
+        {field:"subject",label:PWM_ADMIN.showString('Field_Intruder_Subject')},
+        {field:"timestamp",label:PWM_ADMIN.showString('Field_Intruder_Timestamp')},
+        {field:"count",label:PWM_ADMIN.showString('Field_Intruder_Count')},
+        {field:"status",label:PWM_ADMIN.showString('Field_Intruder_Status')}
+    ];
 };
 
 
@@ -417,47 +395,55 @@ PWM_ADMIN.refreshIntruderGrid=function() {
 };
 
 PWM_ADMIN.auditUserHeaders = function() {
-    return {
-        "timestamp": PWM_ADMIN.showString('Field_Audit_Timestamp'),
-        "perpetratorID": PWM_ADMIN.showString('Field_Audit_PerpetratorID'),
-        "perpetratorDN": PWM_ADMIN.showString('Field_Audit_PerpetratorDN'),
-        "perpetratorLdapProfile": PWM_ADMIN.showString('Field_Audit_PerpetratorLdapProfile'),
-        "eventCode": PWM_ADMIN.showString('Field_Audit_EventCode'),
-        "message": PWM_ADMIN.showString('Field_Audit_Message'),
-        "sourceAddress": PWM_ADMIN.showString('Field_Audit_SourceAddress'),
-        "sourceHost": PWM_ADMIN.showString('Field_Audit_SourceHost'),
-        "guid": PWM_ADMIN.showString('Field_Audit_GUID'),
-        "narrative": PWM_ADMIN.showString('Field_Audit_Narrative')
-    };
+    return [
+        {field:"timestamp",label:PWM_ADMIN.showString('Field_Audit_Timestamp')},
+        {field:"perpetratorID",label:PWM_ADMIN.showString('Field_Audit_PerpetratorID')},
+        {field:"perpetratorDN",label:PWM_ADMIN.showString('Field_Audit_PerpetratorDN'),hidden:true},
+        {field:"perpetratorLdapProfile",label:PWM_ADMIN.showString('Field_Audit_PerpetratorLdapProfile'),hidden:true},
+        {field:"eventCode",label:PWM_ADMIN.showString('Field_Audit_EventCode')},
+        {field:"message",label:PWM_ADMIN.showString('Field_Audit_Message'),hidden:true},
+        {field:"sourceAddress",label:PWM_ADMIN.showString('Field_Audit_SourceAddress')},
+        {field:"sourceHost",label:PWM_ADMIN.showString('Field_Audit_SourceHost'),hidden:true},
+        {field:"guid",label:PWM_ADMIN.showString('Field_Audit_GUID'),hidden:true},
+        {field:"narrative",label:PWM_ADMIN.showString('Field_Audit_Narrative')}
+    ];
 };
 
 PWM_ADMIN.auditHelpdeskHeaders = function() {
-    return {
-        "timestamp": PWM_ADMIN.showString('Field_Audit_Timestamp'),
-        "perpetratorID": PWM_ADMIN.showString('Field_Audit_PerpetratorID'),
-        "perpetratorDN": PWM_ADMIN.showString('Field_Audit_PerpetratorDN'),
-        "perpetratorLdapProfile": PWM_ADMIN.showString('Field_Audit_PerpetratorLdapProfile'),
-        "eventCode": PWM_ADMIN.showString('Field_Audit_EventCode'),
-        "message": PWM_ADMIN.showString('Field_Audit_Message'),
-        "targetID": PWM_ADMIN.showString('Field_Audit_TargetID'),
-        "targetDN": PWM_ADMIN.showString('Field_Audit_TargetDN'),
-        "targetLdapProfile": PWM_ADMIN.showString('Field_Audit_TargetLdapProfile'),
-        "sourceAddress": PWM_ADMIN.showString('Field_Audit_SourceAddress'),
-        "sourceHost": PWM_ADMIN.showString('Field_Audit_SourceHost'),
-        "guid": PWM_ADMIN.showString('Field_Audit_GUID'),
-        "narrative": PWM_ADMIN.showString('Field_Audit_Narrative')
-    };
+    return [
+        {field:"timestamp",label:PWM_ADMIN.showString('Field_Audit_Timestamp')},
+        {field:"perpetratorID",label:PWM_ADMIN.showString('Field_Audit_PerpetratorID')},
+        {field:"perpetratorDN",label:PWM_ADMIN.showString('Field_Audit_PerpetratorDN'),hidden:true},
+        {field:"perpetratorLdapProfile",label:PWM_ADMIN.showString('Field_Audit_PerpetratorLdapProfile'),hidden:true},
+        {field:"eventCode",label:PWM_ADMIN.showString('Field_Audit_EventCode')},
+        {field:"message",label:PWM_ADMIN.showString('Field_Audit_Message'),hidden:true},
+        {field:"targetID",label:PWM_ADMIN.showString('Field_Audit_TargetID')},
+        {field:"targetDN",label:PWM_ADMIN.showString('Field_Audit_TargetDN')},
+        {field:"targetLdapProfile",label:PWM_ADMIN.showString('Field_Audit_TargetLdapProfile')},
+        {field:"sourceAddress",label:PWM_ADMIN.showString('Field_Audit_SourceAddress')},
+        {field:"sourceHost",label:PWM_ADMIN.showString('Field_Audit_SourceHost'),hidden:true},
+        {field:"guid",label:PWM_ADMIN.showString('Field_Audit_GUID'),hidden:true},
+        {field:"narrative",label:PWM_ADMIN.showString('Field_Audit_Narrative'),hidden:true}
+    ];
 };
 
 PWM_ADMIN.auditSystemHeaders = function() {
-    return {
-        "timestamp":PWM_ADMIN.showString('Field_Audit_Timestamp'),
-        "eventCode":PWM_ADMIN.showString('Field_Audit_EventCode'),
-        "message":PWM_ADMIN.showString('Field_Audit_Message'),
-        "instance":PWM_ADMIN.showString('Field_Audit_Instance'),
-        "guid":PWM_ADMIN.showString('Field_Audit_GUID'),
-        "narrative":PWM_ADMIN.showString('Field_Audit_Narrative')
-    };
+    return [
+        {field:"timestamp",label:PWM_ADMIN.showString('Field_Audit_Timestamp')},
+        {field:"eventCode",label:PWM_ADMIN.showString('Field_Audit_EventCode')},
+        {field:"message",label:PWM_ADMIN.showString('Field_Audit_Message')},
+        {field:"instance",label:PWM_ADMIN.showString('Field_Audit_Instance'),hidden:true},
+        {field:"guid",label:PWM_ADMIN.showString('Field_Audit_GUID'),hidden:true},
+        {field:"narrative",label:PWM_ADMIN.showString('Field_Audit_Narrative'),hidden:true}
+    ];
+};
+
+PWM_ADMIN.makeGrid = function() {
+    require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dgrid/extensions/DijitRegistry"],
+        function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry){
+            return declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry ]);
+        }
+    );
 };
 
 PWM_ADMIN.initAuditGrid=function() {
@@ -471,27 +457,9 @@ PWM_ADMIN.initAuditGrid=function() {
             PWM_VAR['auditSystemGrid'] = new CustomGrid({columns: PWM_ADMIN.auditSystemHeaders()}, "auditSystemGrid");
             PWM_VAR['auditHelpdeskGrid'] = new CustomGrid({columns: PWM_ADMIN.auditHelpdeskHeaders()}, "auditHelpdeskGrid");
 
-            // unclick superfluous fields
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-perpetratorDN').click();
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-perpetratorLdapProfile').click();
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-message').click();
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-sourceHost').click();
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-guid').click();
-            PWM_MAIN.getObject('auditUserGrid-hider-menu-check-narrative').click();
-
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-perpetratorDN').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-perpetratorLdapProfile').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-message').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-targetDN').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-targetLdapProfile').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-sourceHost').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-guid').click();
-            PWM_MAIN.getObject('auditHelpdeskGrid-hider-menu-check-narrative').click();
-
-            PWM_MAIN.getObject('auditSystemGrid-hider-menu-check-instance').click();
-            PWM_MAIN.getObject('auditSystemGrid-hider-menu-check-guid').click();
-            PWM_MAIN.getObject('auditSystemGrid-hider-menu-check-narrative').click();
-            PWM_ADMIN.refreshAuditGridData();
+            PWM_ADMIN.refreshAuditGridData(undefined,'USER');
+            PWM_ADMIN.refreshAuditGridData(undefined,'HELPDESK');
+            PWM_ADMIN.refreshAuditGridData(undefined,'SYSTEM');
 
             PWM_VAR['auditUserGrid'].on(".dgrid-row:click", function(evt){
                 PWM_ADMIN.detailView(evt, PWM_ADMIN.auditUserHeaders(), PWM_VAR['auditUserGrid']);
@@ -505,28 +473,32 @@ PWM_ADMIN.initAuditGrid=function() {
         });
 };
 
-PWM_ADMIN.refreshAuditGridData=function(maximum) {
-    PWM_VAR['auditUserGrid'].refresh();
-    PWM_VAR['auditHelpdeskGrid'].refresh();
-    PWM_VAR['auditSystemGrid'].refresh();
-    if (!maximum) {
-        maximum = 1000;
+PWM_ADMIN.refreshAuditGridData=function(maximum,type) {
+    switch (type) {
+        case 'USER':
+            var grid = PWM_VAR['auditUserGrid'];
+            break;
+
+        case 'HELPDESK':
+            var grid = PWM_VAR['auditHelpdeskGrid'];
+            break;
+
+        case 'SYSTEM':
+            var grid = PWM_VAR['auditSystemGrid'];
+            break;
     }
-    var url = PWM_MAIN.addParamToUrl(window.location.href,"processAction", "auditData");
-    url = PWM_MAIN.addParamToUrl(url,'maximum',maximum);
-    var loadFunction = function(data) {
-        PWM_VAR['auditUserGrid'].renderArray(data['data']['user']);
-        PWM_VAR['auditUserGrid'].set("sort", { attribute : 'timestamp', ascending: false, descending: true });
-        PWM_VAR['auditUserGrid'].resize();
 
-        PWM_VAR['auditHelpdeskGrid'].renderArray(data['data']['helpdesk']);
-        PWM_VAR['auditHelpdeskGrid'].set("sort", { attribute : 'timestamp', ascending: false, descending: true });
-        PWM_VAR['auditHelpdeskGrid'].resize();
+    grid.refresh();
 
-        PWM_VAR['auditSystemGrid'].renderArray(data['data']['system']);
-        PWM_VAR['auditSystemGrid'].set("sort", { attribute : 'timestamp', ascending: false, descending: true });
-        PWM_VAR['auditSystemGrid'].resize();
+    if (!maximum) {
+        maximum = 100;
+    }
 
+    var url = PWM_MAIN.addParamToUrl(window.location.href, "processAction", "auditData");
+    url = PWM_MAIN.addParamToUrl(url,'maximum',maximum);
+    url = PWM_MAIN.addParamToUrl(url,'type',type);
+    var loadFunction = function(data) {
+        grid.renderArray(data['data']['records']);
     };
     PWM_MAIN.ajaxRequest(url,loadFunction,{method:'GET'});
 };
@@ -768,8 +740,9 @@ PWM_ADMIN.detailView = function(evt, headers, grid){
     var postExecuteFunctions = [];
     for (var item in headers) {
         (function(key){
-            var value = key in row.data ? row.data[key] : '';
-            var label = headers[key];
+            var field = headers[key]['field'];
+            var label = headers[key]['label'];
+            var value = field in row.data ? row.data[field] : '';
             var id = "record-detail-" + key;
             text += '<tr><td class="key">' + label + '</td>';
             text += '<td><span id="' + id + '" style="max-height: 200px; overflow: auto; max-width: 400px" class="timestamp">';
@@ -788,10 +761,10 @@ PWM_ADMIN.detailView = function(evt, headers, grid){
     }
     text += '</table>';
     PWM_MAIN.showDialog({title:"Record Detail",text:text,showClose:true,allowMove:true,loadFunction:function(){
-        for (var i = 0; i < postExecuteFunctions.length; i++) {
-            postExecuteFunctions[i]();
-        }
-    }});
+            for (var i = 0; i < postExecuteFunctions.length; i++) {
+                postExecuteFunctions[i]();
+            }
+        }});
 };
 
 PWM_ADMIN.showString=function (key, options) {

+ 1 - 1
server/src/main/webapp/public/resources/js/configeditor-settings.js

@@ -862,7 +862,7 @@ FormTableHandler.showOptionsDialog = function(keyName, iteration) {
         }
         bodyText += '<td id="' + inputID + '-label-placeholder" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Placeholder') + '">Placeholder</td><td><input type="text" class="configStringInput" style="width:300px" id="' + inputID + 'placeholder' + '"/></td>';
         bodyText += '</tr><tr>';
-        bodyText += '<td id="' + inputID + '-label-js" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Javascript') + '">JavaScript</td><td><input type="text" class="configStringInput" style="width:300px" id="' + inputID + 'javascript' + '"/></td>';
+        bodyText += '<td id="' + inputID + '-label-js" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Javascript') + '">JavaScript (Depreciated)</td><td><input type="text" class="configStringInput" style="width:300px" id="' + inputID + 'javascript' + '"/></td>';
         bodyText += '</tr><tr>';
         if (currentValue['type'] === 'select') {
             bodyText += '<td class="key">Select Options</td><td><button id="' + inputID + 'editOptionsButton"><span class="btn-icon pwm-icon pwm-icon-list-ul"/> Edit</button></td>';

+ 101 - 0
server/src/main/webapp/public/resources/style.css

@@ -1525,6 +1525,9 @@ html[dir="rtl"] .message.message-error .errorDetail {
     white-space: nowrap;
 }
 
+/*** TABS ****/
+
+
 /*Change these values for the tab highlight color:*/
 .tab-container > .label:hover {
     color: #2D2D2D;
@@ -1532,3 +1535,101 @@ html[dir="rtl"] .message.message-error .errorDetail {
     border-left-color: #2D2D2D;
     border-right-color: #2D2D2D;
 }
+
+
+.tab-container * {
+    box-sizing: border-box;
+}
+
+.tab-container {
+    display: flex;
+    flex-wrap: wrap;
+    position: relative;
+}
+
+.input {
+    position: absolute;
+    opacity: 0;
+}
+
+.tab-container > .label {
+    background: #f6f9f8;
+    border: 1px solid #dae1e1;
+    border-bottom: transparent;
+    border-radius: 4px 4px 0 0;
+    color: #7f7f7f;
+    cursor: pointer;
+    font-size: 15px;
+    height: 31px;
+    margin-left: 5px;
+    padding: 6px 12px;
+    transition: background 0.1s, color 0.1s;
+    width: 100%;
+}
+
+.tab-container > .label:active {
+    /*background: #ccc;*/
+}
+
+.tab-container > .input:focus + .label {
+    z-index: 1;
+}
+
+.tab-container > .input:checked + .label {
+    background: #fff;
+    border: 1px solid #6a6f71;
+    border-bottom-color: transparent;
+    color: #000;
+    position: relative;
+    z-index: 100;
+}
+
+.tab-container > .input:checked + .label::after {
+    content: "";
+    background: #fff;
+    bottom: -3px;
+    height: 3px;
+    left: 0;
+    position: absolute;
+    right: 0;
+}
+
+@media (min-width: 600px) {
+    .tab-container > .label {
+        width: auto;
+    }
+}
+
+.tab-content-pane {
+    background: #fff;
+    display: none;
+    padding: 20px 30px 30px;
+    width: 100%;
+}
+
+@media (min-width: 600px) {
+    .tab-content-pane {
+        order: 99;
+    }
+}
+
+.input:checked + .label + .tab-content-pane {
+    display: block;
+}
+
+.tab-container > .tab-end {
+    border-bottom: 1px solid #6a6f71;
+    left: 0;
+    width: 100%;
+}
+
+/*This targets only IE*/
+@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
+    .tab-container > .tab-end {
+        height: 7px;
+    }
+}
+
+table.ias-table, table.ias-table td {
+    border: none;
+}

+ 0 - 114
server/src/main/webapp/public/resources/tab-container.css

@@ -1,114 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-.tab-container * {
-    box-sizing: border-box;
-}
-
-.tab-container {
-    display: flex;
-    flex-wrap: wrap;
-    position: relative;
-}
-
-.input {
-    position: absolute;
-    opacity: 0;
-}
-
-.tab-container > .label {
-    background: #f6f9f8;
-    border: 1px solid #dae1e1;
-    border-bottom: transparent;
-    border-radius: 4px 4px 0 0;
-    color: #7f7f7f;
-    cursor: pointer;
-    font-size: 15px;
-    height: 31px;
-    margin-left: 5px;
-    padding: 6px 12px;
-    transition: background 0.1s, color 0.1s;
-    width: 100%;
-}
-
-.tab-container > .label:active {
-    /*background: #ccc;*/
-}
-
-.tab-container > .input:focus + .label {
-    z-index: 1;
-}
-
-.tab-container > .input:checked + .label {
-    background: #fff;
-    border: 1px solid #6a6f71;
-    border-bottom-color: transparent;
-    color: #000;
-    position: relative;
-    z-index: 100;
-}
-
-.tab-container > .input:checked + .label::after {
-    content: "";
-    background: #fff;
-    bottom: -3px;
-    height: 3px;
-    left: 0;
-    position: absolute;
-    right: 0;
-}
-
-@media (min-width: 600px) {
-    .tab-container > .label {
-        width: auto;
-    }
-}
-
-.tab-content-pane {
-    background: #fff;
-    display: none;
-    padding: 20px 30px 30px;
-    width: 100%;
-}
-
-@media (min-width: 600px) {
-    .tab-content-pane {
-        order: 99;
-    }
-}
-
-.input:checked + .label + .tab-content-pane {
-    display: block;
-}
-
-.tab-container > .tab-end {
-    border-bottom: 1px solid #6a6f71;
-    left: 0;
-    width: 100%;
-}
-
-/*This targets only IE*/
-@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
-    .tab-container > .tab-end {
-        height: 7px;
-    }
-}

+ 23 - 0
server/src/main/webapp/public/resources/themes/pwm/style.css

@@ -164,3 +164,26 @@ h3 {
     border: 1px solid transparent;
     box-shadow: 0 0 0 0 white, 0 0 0 0 white, 9px 0 12px -4px #B2B1B9, -9px 0 12px -4px #B2B1B9;
 }
+
+.tab-container > .tab-end {
+    border-bottom: 1px solid #E8E1F6;
+}
+
+.tab-container > .input:checked + .label {
+    border: 1px solid #E8E1F6;
+    color: #808080;
+}
+
+.tab-container > .label {
+    font-size: 14px;
+    height: 25px;
+    padding-top: 3px;
+}
+
+.tab-container > .label:hover {
+    color: #808080;
+    border-top-color: #E8E1F6;
+    border-left-color: #E8E1F6;
+    border-right-color: #E8E1F6;
+}
+