Compare commits

..
640 Commits
Author SHA1 Message Date
Bill Burke 0cd64e297a Merge pull request #447 from patriot1burke/master
bump version
2014-06-02 12:22:40 -04:00
Bill Burke 0fcf80dd6e bump version 2014-06-02 12:21:58 -04:00
Bill Burke 56e9e0b4f9 Merge pull request #446 from patriot1burke/master
NPE in as7 adapter logout
2014-06-02 12:03:09 -04:00
Bill Burke dfcd3267e4 NPE in as7 adapter logout 2014-06-02 12:02:42 -04:00
Bill Burke f689747fd6 Merge pull request #445 from patriot1burke/master
wildfly ssl fixes
2014-06-02 11:22:51 -04:00
Bill Burke bb11165568 wildfly ssl fixes 2014-06-02 11:22:19 -04:00
Bill Burke a180c6b896 Merge remote-tracking branch 'upstream/master' 2014-06-02 09:37:30 -04:00
Bill Burke 34d3be244d bump undertow version 2014-06-02 09:37:18 -04:00
Stian Thorgersen 4662781213 Merge pull request #443 from lnewson/master
Fixed an integer overflow that caused the token to be refreshed too often
2014-06-02 09:35:11 +01:00
Stian Thorgersen 5bd12a708f Merge pull request #444 from lnewson/social-fix
Fixed an NPE when the social users name is null
2014-06-02 09:34:32 +01:00
Bill Burke e39149c954 Merge remote-tracking branch 'upstream/master' 2014-06-01 18:03:49 -04:00
Bill Burke b925a97b5d bump wildfly version 2014-06-01 18:03:39 -04:00
Lee Newson 3bcf2c81da Fixed an NPE when the users name is null 2014-05-31 10:39:34 +10:00
Lee Newson b52801de25 Fixed an integer overflow that caused the token to always be refreshed 2014-05-31 10:26:06 +10:00
Stian Thorgersen 4d8c3d7d73 Merge pull request #442 from stianst/master
Cache for FreeMarker templates and set cache-control for static resources from themes
2014-05-30 17:03:37 +01:00
Stian Thorgersen ea1f10361a Cache FreeMarker templates 2014-05-30 16:59:25 +01:00
Stian Thorgersen 5dafad71ac Set cache-control on static resources from themes 2014-05-30 16:59:25 +01:00
Bill Burke df28fb97ed Merge pull request #440 from stianst/master
Bump version to 1.0.beta2-SNAPSHOT
2014-05-30 08:56:00 -04:00
Stian Thorgersen 5600018a56 Merge pull request #441 from mposolda/master
Basic fix of performance tests and added README. Still requires work...
2014-05-30 10:37:05 +01:00
mposolda 441c00c06f Basic fix of performance tests and added README. Still requires work... 2014-05-30 11:03:16 +02:00
Stian Thorgersen 9533bbc47d Merge pull request #439 from mposolda/master
Fix js-console on FF
2014-05-30 08:55:33 +01:00
Stian Thorgersen 2025d314ec Bump version to 1.0.beta2-SNAPSHOT 2014-05-30 08:52:03 +01:00
mposolda 772f991545 Fix js-console on FF 2014-05-29 18:56:34 +02:00
Bill Burke f6b59b03bb Merge pull request #438 from patriot1burke/master
bump version
2014-05-29 12:16:30 -04:00
Bill Burke 2ae3775841 bump version 2014-05-29 12:12:33 -04:00
Stian Thorgersen 8573c13b8c Merge pull request #437 from mposolda/master
Improve PropertiesAuthenticationProvider to support retrieving user pass...
2014-05-29 16:10:11 +01:00
mposolda db1c6e9afd Improve PropertiesAuthenticationProvider to support retrieving user passwords from custom location and persisting password updates 2014-05-29 16:55:41 +02:00
Stian Thorgersen 429f003578 Merge pull request #436 from stianst/master
Fix audit provider example
2014-05-29 15:08:48 +01:00
Stian Thorgersen dd684858f2 Fix audit provider example 2014-05-29 15:08:27 +01:00
Stian Thorgersen 2e82d56634 Merge pull request #435 from stianst/master
Demo fixes
2014-05-29 14:04:40 +01:00
Stian Thorgersen 1ede6e70b7 Fix customer-portal-cli example 2014-05-29 14:04:10 +01:00
Stian Thorgersen 8908fbef58 Fix instructions for deploying demo to use jboss-as:deploy or wildfly:deploy 2014-05-29 14:03:50 +01:00
Stian Thorgersen 09a96b3a2b Merge pull request #434 from stianst/master
Added sunrise theme to examples, including instructions on how to deploy
2014-05-29 13:15:14 +01:00
Stian Thorgersen 5b7c8b6920 Added sunrise theme to examples, including instructions on how to deploy 2014-05-29 13:14:39 +01:00
Stian Thorgersen 409b46963e Merge pull request #433 from mposolda/master
Add more scopes into testrealm.json to ensure that admin is able to acce...
2014-05-29 13:03:04 +01:00
mposolda 53cf574735 Add more scopes into testrealm.json to ensure that admin is able to access all things he needs 2014-05-29 13:06:47 +02:00
Stian Thorgersen fe236cabd3 Merge pull request #431 from mposolda/master
Change default timeouts for testrealm.json bundled in examples, Fix admin-access-app example
2014-05-29 10:55:35 +01:00
Stian Thorgersen d25536569a Merge pull request #430 from PatrickMadden/master
Added bindIp support to embedmongo-maven-plugin configuration. Solves is...
2014-05-29 10:54:46 +01:00
mposolda 06b9499805 Fixing typo in admin console causing errors when change passwordCredentialsGrant 2014-05-29 11:42:54 +02:00
Stian Thorgersen 2a2e57467d Merge pull request #432 from stianst/master
Fix themes displayed multiple times in admin console
2014-05-29 10:28:22 +01:00
Stian Thorgersen 24ac6cbbac Fix themes displayed multiple times in admin console 2014-05-29 10:28:05 +01:00
Stian Thorgersen 0b68e52b83 Fix Cannot read property 'realm' of undefined 2014-05-29 10:20:49 +01:00
Stian Thorgersen cf1393ffe1 Updated sunrise theme example 2014-05-29 09:42:17 +01:00
mposolda c4808a3e61 Fix admin-App example 2014-05-29 10:29:06 +02:00
mposolda f0d37b19e2 Fix typo in docs 2014-05-29 10:28:54 +02:00
mposolda 65206a72b2 Change default timeouts for testrealm.json bundled in examples 2014-05-29 09:30:22 +02:00
Stian Thorgersen a91d4c06ee Merge pull request #429 from mposolda/master
Fixes in JS examples
2014-05-29 07:52:46 +01:00
PatrickMadden e8ad08bd28 Reuse bindIp of model for audit in integration test suite. 2014-05-28 20:02:42 -05:00
PatrickMadden 7a3b1ddd18 Added bindIp support to embedmongo-maven-plugin configuration. Solves issue of firewall pop-ups consistently displaying on every Microsoft Windows build. 2014-05-28 19:14:23 -05:00
mposolda db939ee076 Fix angular-product example 2014-05-28 23:30:34 +02:00
mposolda 5ede17d8b8 Replace innerText with innerHTML as innerText doesn't work on FF 2014-05-28 21:50:23 +02:00
Bill Burke 0ff992e04e Merge pull request #428 from patriot1burke/master
docs
2014-05-28 12:34:11 -04:00
Bill Burke 34a579821a docs 2014-05-28 12:33:49 -04:00
Stian Thorgersen dd30a35e1e Disable session iframe in Corodova 2014-05-28 17:29:13 +01:00
Stian Thorgersen 2074e1423f Fixes for example/cordova 2014-05-28 17:10:28 +01:00
Stian Thorgersen d5476b78ec Merge pull request #427 from stianst/master
Deploy server war and dist bundles to Maven repo. Also added '-parent' to some parent poms where this was missing
2014-05-28 15:32:58 +01:00
Stian Thorgersen df19700f69 Deploy server war and dist bundles to Maven repo. Also added '-parent' to some parent poms where this was missing 2014-05-28 15:32:20 +01:00
Bill Burke 3c605a7740 Merge pull request #426 from patriot1burke/master
docs and master realm rename
2014-05-28 09:51:56 -04:00
Bill Burke a64b35d72f doc merge 2014-05-28 09:33:42 -04:00
Bill Burke e880b77692 update docs 2014-05-28 09:31:33 -04:00
Stian Thorgersen e8fe26ff1c Merge pull request #425 from stianst/master
Fix for WildFly 8.1.0.CR2
2014-05-28 14:20:53 +01:00
Stian Thorgersen 108ccd98d0 Fix for WildFly 8.1.0.CR2 2014-05-28 14:20:29 +01:00
Stian Thorgersen 5fddc28cd6 Updated jboss maven plugin and added wildfly maven plugin. Both are skipped by default, so only needs to be specified in modules that should be deployed 2014-05-28 11:00:36 +01:00
Stian Thorgersen 63001d5e16 Merge pull request #424 from mposolda/master
Fix configuration of maven plugins in authentication-properties example
2014-05-28 09:59:36 +01:00
mposolda caccf76ea4 Fix configuration of maven plugins in authentication-properties example 2014-05-28 10:56:00 +02:00
Stian Thorgersen 5120549005 Merge pull request #423 from mposolda/master
Updated documentation and added example for Authentication SPI
2014-05-28 09:12:01 +01:00
mposolda e67032a9d7 Documentation about Authentication SPI, LDAP and Export/import 2014-05-27 22:37:25 +02:00
mposolda a903eb7605 Added example for AuthenticationProvider SPI 2014-05-27 22:37:25 +02:00
Bill Burke 5b431338c2 Merge pull request #422 from patriot1burke/master
admin console mistake
2014-05-27 14:56:14 -04:00
Bill Burke c20d488648 admin console mistake 2014-05-27 14:38:03 -04:00
Stian Thorgersen 57a8f3f5ee Added to migration guid for beta1 2014-05-27 15:29:04 +01:00
Stian Thorgersen 6dda7fc356 Added example providers for audit listener and audit provider 2014-05-27 15:11:52 +01:00
Stian Thorgersen 9261c1608f Fix missing @Path in admin console 2014-05-27 13:40:32 +01:00
Stian Thorgersen 80cd43390b Added doc for audit 2014-05-27 13:40:23 +01:00
Stian Thorgersen c196849574 Updated doc for themes 2014-05-27 13:08:00 +01:00
Stian Thorgersen 1007d19860 Updated instructions to configure Mongo through keycloak-server.json 2014-05-27 12:05:35 +01:00
Stian Thorgersen 374a7b8848 Updated url for OpenShift cartridge 2014-05-27 12:05:22 +01:00
Stian Thorgersen 8ae57fd117 Include all themes in configuration/themes to make it easy to modify/create themes 2014-05-27 12:05:09 +01:00
Bill Burke 5a2fd16b92 Merge pull request #421 from patriot1burke/master
jaxrs-doclet, more javadocs
2014-05-26 20:51:53 -04:00
Bill Burke 3545ac130b jax-doclet 2014-05-26 20:50:42 -04:00
Bill Burke 199c786e22 jaxrs-doclet support 2014-05-26 20:41:33 -04:00
Bill Burke 38b52283a5 Merge remote-tracking branch 'upstream/master' 2014-05-26 12:50:09 -04:00
Bill Burke 1f8b2d4c7b stuff 2014-05-26 12:49:56 -04:00
Bill Burke 9c0fa93510 Merge pull request #420 from patriot1burke/master
admin rest example, default origins, effect roles
2014-05-23 19:03:25 -04:00
Bill Burke ab2932f75e effective roles to oauth 2014-05-23 19:01:09 -04:00
Bill Burke 81083ef470 Merge remote-tracking branch 'upstream/master' 2014-05-23 18:22:34 -04:00
Bill Burke e4232c73c6 admin rest api example 2014-05-23 18:20:55 -04:00
Bill Burke 29070cec77 add KeycloakSecurityContext to principal 2014-05-23 16:26:44 -04:00
Bill Burke fbef9f7691 default web origins 2014-05-23 16:15:00 -04:00
Bill Burke 97f3a3feef Merge pull request #419 from patriot1burke/master
admin cors
2014-05-23 16:12:08 -04:00
Bill Burke 54cc095ff5 admin cors support 2014-05-23 16:02:14 -04:00
Stian Thorgersen 0ad4d5cc47 Change 'Composite roles' to 'Effective roles' on role mapping/scope pages 2014-05-23 16:44:55 +01:00
Bill Burke 236f3ab768 admin cors 2014-05-23 09:37:07 -04:00
Stian Thorgersen 6de7c42ac9 Merge pull request #418 from mposolda/master
Fixing typo
2014-05-23 10:21:44 +01:00
mposolda bb7a809ab5 Fixing typo 2014-05-23 11:19:17 +02:00
Stian Thorgersen 142b155460 Merge pull request #417 from mposolda/master
At least one redirect_uri must be available for oauthClients and applica...
2014-05-23 10:11:06 +01:00
Stian Thorgersen 0f6bc9c5c7 KEYCLOAK-492
Can't add scope on application role to application
2014-05-23 10:10:19 +01:00
mposolda e600d7c449 At least one redirect_uri must be available for oauthClients and applications, which are not bearer-only 2014-05-23 10:55:43 +02:00
Stian Thorgersen 37327be521 KEYCLOAK-482
Cannot read property 'realm' of undefined
2014-05-23 09:36:14 +01:00
Stian Thorgersen 07d2bf988a Merge pull request #416 from mposolda/master
Fix logging typo when importing roles
2014-05-23 09:14:10 +01:00
mposolda df06a0c878 Fix logging typo when importing roles 2014-05-23 09:46:50 +02:00
Stian Thorgersen cee3fea98f Merge pull request #415 from mposolda/master
Invalid active tab when on token-settings page
2014-05-23 08:39:49 +01:00
mposolda 5c378cbd9d Invalid active tab when on token-settings page 2014-05-23 08:51:06 +02:00
Stian Thorgersen 37099f3177 KEYCLOAK-494
Session state iframe uses first redirect uri for a client
2014-05-22 17:51:51 +01:00
Stian Thorgersen 128cc7eadb Fixes to js-console example 2014-05-22 17:07:40 +01:00
Stian Thorgersen de36973716 Redirect to login form automatically in keycloak.js if login-required is set 2014-05-22 16:59:38 +01:00
Stian Thorgersen 08b1996149 Removed jboss.as.jpa.managed property from persistence.xml as it's causing problems on AS 7.1 2014-05-22 16:01:05 +01:00
Stian Thorgersen 0574302840 Fix dialog for resetting user password 2014-05-22 15:39:31 +01:00
Stian Thorgersen fad0a7728c Make add redirect-uri and web-origin use same l&f 2014-05-22 15:10:22 +01:00
Stian Thorgersen 86ad7292af Fix updating sessions after logout session 2014-05-22 14:51:36 +01:00
Stian Thorgersen 74e2bfd9bd Merge pull request #414 from mposolda/master
Extract some version numbers into properties
2014-05-22 11:21:55 +01:00
mposolda 0c60bd80df Extract some versions into properties 2014-05-22 12:04:13 +02:00
Stian Thorgersen 46f81a331a Merge pull request #413 from mposolda/master
Fixing location of folder for FolderThemeProvider
2014-05-22 08:54:22 +01:00
mposolda b0ba7ed188 Fixing location of FolderThemeProvider 2014-05-22 09:21:42 +02:00
Bill Burke 0d9e14d4c7 Merge remote-tracking branch 'upstream/master' 2014-05-21 10:56:21 -04:00
Stian Thorgersen 10ae457237 Fixes to admin console 2014-05-21 14:10:59 +01:00
Stian Thorgersen 73f59417bd Remove index.html from redirect on '/auth/admin/' 2014-05-21 12:47:07 +01:00
Stian Thorgersen 6814230193 Fix NPE in account service 2014-05-21 12:13:00 +01:00
Stian Thorgersen d68131ac00 KEYCLOAK-475
Log is shown in menu when audit is disabled for realm
2014-05-21 12:05:32 +01:00
Stian Thorgersen 952f098440 KEYCLOAK-483
Remove 'index.html' from console url
2014-05-21 11:16:48 +01:00
Stian Thorgersen f08477ea66 Run import before creating default realm 2014-05-21 11:16:47 +01:00
Stian Thorgersen 8544dd66d8 Merge pull request #412 from mposolda/master
Mongo related fixes
2014-05-21 10:50:31 +01:00
Stian Thorgersen a5a2848251 KEYCLOAK-476
theme/account/patternfly/img/icon-sidebar-active.svg not found
2014-05-21 10:40:15 +01:00
mposolda 7232535729 More mongo fixes 2014-05-21 11:34:27 +02:00
mposolda 271292dbd4 Mongo related fixes 2014-05-21 11:34:27 +02:00
Stian Thorgersen eb47d43497 For installed app urn redirect to a page instead of returning the html, this is to prevent NPE if page is refreshed 2014-05-21 10:32:03 +01:00
Stian Thorgersen fd9317a295 KEYCLOAK-474
Code not visible for urn:ietf:wg:oauth:2.0:oob
2014-05-21 09:52:02 +01:00
Stian Thorgersen 11fc6d5321 KEYCLOAK-477
Remove keycloak.js debug statements
2014-05-21 09:39:02 +01:00
Stian Thorgersen 64a36aee03 Merge pull request #411 from stianst/master
Twitter fix and read keycloak-sever.json from standalone/configuration
2014-05-21 09:35:49 +01:00
Stian Thorgersen 59440840fe Read keycloak-sever.json from <JBOSS_HOME>/standalone/configuration if it exists 2014-05-21 09:35:01 +01:00
Stian Thorgersen c2075af7f7 Fix Twitter for localhost 2014-05-21 08:47:58 +01:00
Stian Thorgersen ab85f09608 Merge pull request #410 from ssilvert/master
Make UndertowUserSessionManagement class usable without the Servlet API.
2014-05-21 07:56:17 +01:00
Stan Silvert da799b18c3 Make UndertowUserSessionManagement class usable without the Servlet API. 2014-05-20 19:58:08 -04:00
Bill Burke ca6de3e486 simpler applicaiton create page 2014-05-20 17:45:14 -04:00
Bill Burke ae3c4e8459 default web origin is redirect uris 2014-05-20 17:40:45 -04:00
Bill Burke 8e79f550b0 Merge remote-tracking branch 'upstream/master' 2014-05-20 16:49:28 -04:00
Bill Burke 0137918701 urn 2014-05-20 16:49:25 -04:00
Bill Burke 79828ddc78 Merge pull request #409 from patriot1burke/master
fix urn redirect uri
2014-05-20 16:15:40 -04:00
Bill Burke fcca9dd90a fix urn redirect uri 2014-05-20 16:15:13 -04:00
Stian Thorgersen 1b4b537043 Merge pull request #408 from stianst/email-events
Email audit listener
2014-05-20 17:29:45 +01:00
Stian Thorgersen b09e2f697e Email audit listener 2014-05-20 17:04:17 +01:00
Bill Burke 2855baecef Merge pull request #407 from patriot1burke/master
check redirect uri exists in token service
2014-05-20 11:39:22 -04:00
Bill Burke b7cf1bb215 Merge remote-tracking branch 'upstream/master' 2014-05-20 11:38:50 -04:00
Bill Burke b3dd349342 check redirect uri exists in token service 2014-05-20 11:38:35 -04:00
Stian Thorgersen 357337047b Merge pull request #406 from stianst/master
Fix config for folder theme provider£
2014-05-20 14:42:39 +01:00
Stian Thorgersen 5496f043a5 Fix config for folder theme provider£ 2014-05-20 14:42:07 +01:00
Bill Burke a22aa8055f Merge pull request #405 from patriot1burke/master
redirect uris not required with bearer only
2014-05-20 09:31:23 -04:00
Bill Burke 57f0ea0638 Merge remote-tracking branch 'upstream/master' 2014-05-20 09:27:18 -04:00
Bill Burke f723ac79d1 redirect not required with bearer only 2014-05-20 09:19:04 -04:00
Stian Thorgersen 3a15b416a9 Merge pull request #404 from stianst/master
Add realm option to enable/disable Resource Owner Password Credentials G...
2014-05-20 11:33:07 +01:00
Stian Thorgersen cc97265c06 Add realm option to enable/disable Resource Owner Password Credentials Grant 2014-05-20 11:32:22 +01:00
Stian Thorgersen 665038d0ce Merge pull request #403 from stianst/master
Add email theme config for realm
2014-05-20 11:01:07 +01:00
Stian Thorgersen 8d1149dc07 Add email theme config for realm 2014-05-20 11:00:43 +01:00
Bill Burke ebea9e9270 Merge pull request #402 from patriot1burke/master
fix remove expired token
2014-05-19 18:53:54 -04:00
Bill Burke b5aa9efefb fix remove expired sessions 2014-05-19 18:53:28 -04:00
Bill Burke 95f56e2e62 fix removeexpired sessions 2014-05-19 18:33:54 -04:00
Bill Burke 273775d848 Merge pull request #401 from patriot1burke/master
fix aerogear again
2014-05-19 18:14:16 -04:00
Bill Burke 8606f344a8 fix aerogear again 2014-05-19 18:13:54 -04:00
Bill Burke 53c3696cbb Merge pull request #400 from patriot1burke/master
aerogear fix
2014-05-19 18:09:54 -04:00
Bill Burke 7203d9ba12 aerogear fix 2014-05-19 18:09:36 -04:00
Bill Burke 26d3e48555 Merge pull request #399 from patriot1burke/master
don't allow bearer-only to login
2014-05-19 15:09:18 -04:00
Bill Burke a53206995a stuff 2014-05-19 15:08:38 -04:00
Bill Burke b2c8f48af9 Merge pull request #398 from patriot1burke/master
require redirect
2014-05-19 14:54:31 -04:00
Bill Burke 11c23a7945 require redirect 2014-05-19 14:44:36 -04:00
Stian Thorgersen 3be4579204 Merge pull request #397 from stianst/email-themes
Email themes
2014-05-19 17:40:52 +01:00
Stian Thorgersen a3d08e7191 Added theme support to emails 2014-05-19 17:34:58 +01:00
Bill Burke 7e8b16f975 acct svc + sessions 2014-05-19 17:34:48 +01:00
Bill Burke 966eb2fe47 Merge pull request #396 from patriot1burke/master
acct svc + sessions
2014-05-19 11:46:04 -04:00
Bill Burke 5826f02ebe acct svc + sessions 2014-05-19 11:45:42 -04:00
Bill Burke 2b525c6016 Merge pull request #395 from patriot1burke/master
fix brute shutdown
2014-05-19 10:35:49 -04:00
Bill Burke dc7923c436 fix brute shutdown 2014-05-19 10:33:49 -04:00
Stian Thorgersen 00a64eeb89 Merge pull request #394 from stianst/master
Added keycloak-server.json for project-integrations/aerogear-ups
2014-05-19 10:47:15 +01:00
Stian Thorgersen 76c9f2c4ec Added keycloak-server.json for project-integrations/aerogear-ups 2014-05-19 10:46:33 +01:00
Bill Burke dafa52ea93 Merge pull request #393 from patriot1burke/master
user-session client association
2014-05-17 14:32:46 -04:00
Bill Burke c2bf6c3822 Merge remote-tracking branch 'upstream/master' 2014-05-17 14:24:37 -04:00
Bill Burke ab8de6ba25 client user-session association 2014-05-17 14:24:32 -04:00
Stian Thorgersen 6714e2b87f Merge pull request #392 from stianst/master
Server config
2014-05-16 17:34:13 +01:00
Stian Thorgersen 855269f35d Added 'keycloak-server.json' for configuring the server
Added SPI interface to detect SPI's
Converted Model to SPI/Provider to be loaded through ProviderSessionFactory
2014-05-16 17:33:22 +01:00
Stian Thorgersen a3ed02ea16 Merge pull request #391 from jeroenr/master
CORS improvements and clean up
2014-05-16 16:06:43 +01:00
Jeroen Rosenberg 84f13eadc1 * Possibility to add exposed headers
* Default allow headers
* Fix warnings, unused import, redundancies
2014-05-16 16:39:17 +02:00
Jeroen Rosenberg cedf43c084 Refactored joining of allowed headers to be more efficient 2014-05-16 14:28:49 +02:00
Jeroen Rosenberg 4c281a39bb Unused import 2014-05-16 14:10:13 +02:00
Jeroen Rosenberg 5533357c46 Added HEAD as default allowed method for Cors 2014-05-16 14:09:50 +02:00
Stian Thorgersen ae4d83107a Merge pull request #390 from jeroenr/master
Fixed URI encoding for clientId and parse optional client secret from config file
2014-05-16 13:02:41 +01:00
Jeroen Rosenberg b92494f023 Refactored to use switch with fall through for readability 2014-05-16 13:52:59 +02:00
Jeroen Rosenberg d3a2617d3c Read optional client secret configuration to ensure we can use Basic auth as well 2014-05-16 13:49:28 +02:00
Jeroen Rosenberg 740943290c URI encode clientId for referrer parameter 2014-05-16 13:46:46 +02:00
Bill Burke c51c7fe189 Merge pull request #389 from jeroenr/master
Improved exception handling. Don't swallow exception
2014-05-16 06:34:18 -04:00
Bill Burke 610eaaaaff Merge pull request #387 from cardosogabriel/POLISHING_UI
UI POLISHING: design styles and content
2014-05-16 06:34:09 -04:00
Jeroen Rosenberg bde9944449 Improved exception handling. Don't swallow exception 2014-05-16 09:35:19 +02:00
Bill Burke 1318ea85be Merge pull request #388 from patriot1burke/master
session idle and max lifespan
2014-05-15 23:18:43 -04:00
Bill Burke 67e3e60f28 test sso idle, logout on idle 2014-05-15 23:10:14 -04:00
Bill Burke bc2360e985 sso session idle and max lifespan 2014-05-15 17:25:57 -04:00
Gabriel Cardoso 8e7b0022a6 UI POLISHING: design styles and content 2014-05-15 15:22:38 -03:00
Bill Burke 27efd3c0a4 ssoSessionIdleTimeout refactor 2014-05-15 13:53:28 -04:00
Bill Burke 07a31d3f35 Merge pull request #386 from patriot1burke/master
check ssl required in adapter
2014-05-14 15:24:22 -04:00
Bill Burke 07dbb7d7b7 check ssl required in adapter 2014-05-14 15:23:45 -04:00
Bill Burke 26f6da10dd Merge pull request #385 from patriot1burke/master
Improved role and scope mapping screens
2014-05-14 14:50:52 -04:00
Bill Burke 7ba8e09aef improved scope screen 2014-05-14 14:50:11 -04:00
Stian Thorgersen d95372c993 Merge pull request #384 from stianst/master
Updated session status iframe for JavaScript adapter
2014-05-14 17:54:58 +01:00
Stian Thorgersen 643daadf60 Updated session status iframe for JavaScript adapter to be created automatically, and to invoke onAuthLogout callback if user is logged out 2014-05-14 17:46:30 +01:00
Stian Thorgersen 7bea4af6c9 Redirect to login page if logged out and submitting forms in acct mngmt 2014-05-14 17:46:30 +01:00
Stian Thorgersen b27776b3c6 Merge pull request #383 from matzew/master
removing java7 ctor usage of runtimeexception
2014-05-14 16:48:15 +01:00
Matthias Wessendorf d9c8dedcfb removing JavaSE6 incompatible ctor 2014-05-14 17:43:36 +02:00
Matthias Wessendorf 9e0bf9e7dc Removing exception ctor that does not work w/ java 6 2014-05-14 17:40:27 +02:00
Bill Burke 0e0dfb60e0 composite role mapping listing 2014-05-14 10:37:50 -04:00
Stian Thorgersen b95fd0d0b0 Merge pull request #382 from stianst/master
KEYCLOAK-431 View open sessions, and logout all sessions, through accoun...
2014-05-14 11:56:52 +01:00
Stian Thorgersen f4f9b1e323 KEYCLOAK-431 View open sessions, and logout all sessions, through account management 2014-05-14 11:56:28 +01:00
Bill Burke f7c3373f75 Merge pull request #381 from patriot1burke/master
IE fixes
2014-05-13 22:47:46 -04:00
Bill Burke 639b7c5610 fix IE problems 2014-05-13 22:33:46 -04:00
Bill Burke b30809d681 httponly, ie keycloak.js fix 2014-05-13 22:06:32 -04:00
Bill Burke 0731f9977b Merge pull request #380 from patriot1burke/master
cors example
2014-05-13 17:17:51 -04:00
Bill Burke e8f8829a76 cors example 2014-05-13 17:17:27 -04:00
Bill Burke 663ff30eae Merge pull request #379 from patriot1burke/master
angular example
2014-05-13 12:07:59 -04:00
Bill Burke 01d22c3aab angular example 2014-05-13 12:07:28 -04:00
Bill Burke 9b24369b92 Merge pull request #378 from patriot1burke/master
update ups writeup
2014-05-12 19:45:24 -04:00
Bill Burke f6645eea92 update ups writeup 2014-05-12 19:45:01 -04:00
Bill Burke 634f61281d Merge pull request #377 from patriot1burke/master
aerogear template
2014-05-12 19:40:00 -04:00
Bill Burke c560a739b1 aerogear readme 2014-05-12 19:39:29 -04:00
Bill Burke ea672e36ae rename realm management app 2014-05-12 19:28:34 -04:00
Bill Burke eb9335f706 aerogear 2014-05-12 18:34:17 -04:00
Bill Burke d429525620 Merge pull request #376 from patriot1burke/master
fix create realm
2014-05-12 15:42:42 -04:00
Bill Burke b1a480c20d fix create realm 2014-05-12 15:42:10 -04:00
Bill Burke 0fcebb8670 remove bundled app 2014-05-12 15:38:15 -04:00
Bill Burke 3e32bd116b aerogear demo update 2014-05-12 15:35:54 -04:00
Bill Burke a4cb268703 Merge pull request #375 from patriot1burke/master
per-realm admin console
2014-05-12 10:13:18 -04:00
Bill Burke 06b98589eb Merge remote-tracking branch 'upstream/master' 2014-05-12 10:12:42 -04:00
Bill Burke 1e1991e285 per-realm admin 2014-05-12 10:12:31 -04:00
Bill Burke b0446fab0c Merge pull request #374 from patriot1burke/master
iframe check login
2014-05-10 11:28:24 -04:00
Bill Burke 1194e40ef2 iframe check login 2014-05-10 11:23:44 -04:00
Stian Thorgersen 2a820c73b1 Merge pull request #373 from stianst/master
KEYCLOAK-421 Set realm admin apps to bearer-only
2014-05-09 12:03:56 +01:00
Stian Thorgersen 624fcf6197 KEYCLOAK-421 Set realm admin apps to bearer-only 2014-05-09 12:03:39 +01:00
Stian Thorgersen 59747500aa Merge pull request #372 from stianst/login-session
KEYCLOAK-432 Added user sessions
2014-05-09 11:53:23 +01:00
Stian Thorgersen 6f0b12174c KEYCLOAK-432 Added user sessions 2014-05-09 11:52:45 +01:00
Bill Burke 5568c8dc7d Merge pull request #371 from patriot1burke/master
keycloak.js admin login
2014-05-08 20:08:52 -04:00
Bill Burke d957cc4883 Merge remote-tracking branch 'upstream/master' 2014-05-08 20:03:20 -04:00
Bill Burke 2d949b63b5 keycloak.js login for admin 2014-05-08 19:55:57 -04:00
Stian Thorgersen abdbef6a51 Merge pull request #370 from mposolda/master
Social fixes and improvements
2014-05-07 16:50:19 +01:00
mposolda c51011acef KEYCLOAK-354 Possibility for admin to see social links of some user in admin console 2014-05-07 17:28:13 +02:00
mposolda 6b1e0401ba KEYCLOAK-361 it shouldn't be possible to remove last social link if user don't have password 2014-05-07 17:28:13 +02:00
Stian Thorgersen d523242dbb Merge pull request #368 from mposolda/master
A bit more cleanup of dependencies
2014-05-07 12:52:22 +01:00
Stian Thorgersen 92058b16c9 Merge pull request #369 from abstractj/cleanup
Little code clean up
2014-05-07 12:52:09 +01:00
Bruno Oliveira 4424c425a6 Redundant initialization 2014-05-07 08:15:31 -03:00
Bruno Oliveira 5f7b0aae06 Variables will be never used 2014-05-07 08:15:02 -03:00
Bruno Oliveira f77e3b5532 Unused imports 2014-05-07 08:14:32 -03:00
Bruno Oliveira 334b981741 notBefore wasn't being assigned 2014-05-07 08:10:03 -03:00
Bruno Oliveira 957ff7d206 Make it final 2014-05-07 08:05:57 -03:00
Bruno Oliveira 5973ccd522 Scope can be private 2014-05-07 08:03:21 -03:00
Bruno Oliveira 1ea1f0af96 Removing redundant local variable 2014-05-07 07:49:45 -03:00
Bruno Oliveira f5ac81db41 Return not necessary here 2014-05-07 07:48:42 -03:00
Bruno Oliveira 9e7f52ccda extends Object isn't necessary 2014-05-07 07:47:39 -03:00
mposolda f8173e6a5a A bit more cleanup of dependencies 2014-05-07 11:08:47 +02:00
Bill Burke 5edf05d569 Merge pull request #366 from mposolda/export-import-rebased
Full export-import implemented. Remove dependency of mongo on picketlink...
2014-05-06 21:17:51 -04:00
Bill Burke 4666bcbfb2 Merge pull request #367 from patriot1burke/master
remove /rest from RealmsResource - pom fixes
2014-05-06 21:09:11 -04:00
Bill Burke a374e52ea5 /rest removal web.xml fixes 2014-05-06 21:08:27 -04:00
Bill Burke f987d331b7 /rest removal and pom fixes 2014-05-06 20:43:01 -04:00
Bill Burke 314f177fe7 Merge remote-tracking branch 'upstream/master' 2014-05-06 20:21:59 -04:00
Bill Burke 64f591769a remove /rest from RealmsResource 2014-05-06 20:21:49 -04:00
mposolda 0801c9c120 Full export-import implemented. Remove dependency of mongo on picketlink-common 2014-05-06 22:15:57 +02:00
Bill Burke e4e02a2615 Merge pull request #365 from patriot1burke/master
aerogear fix
2014-05-06 10:41:42 -04:00
Bill Burke ff69607f52 aerogear fix 2014-05-06 10:41:17 -04:00
Bill Burke a23b213736 Merge pull request #362 from matzew/JSP-Error
Removing JSP code, adding simple text for a simple demo...
2014-05-06 10:33:27 -04:00
Bill Burke 36ce4e04d9 Merge pull request #364 from patriot1burke/master
add aerogear stuff
2014-05-06 10:10:45 -04:00
Bill Burke 3b6593b476 add aerogear stuff 2014-05-06 10:10:17 -04:00
Bill Burke 6ef3102cd1 Merge pull request #361 from patriot1burke/master
remove ups social dependencies
2014-05-06 10:08:08 -04:00
Bill Burke 39381c104c merge fixes 2014-05-06 10:07:48 -04:00
Bill Burke c04b490f29 port fix 2014-05-06 09:59:52 -04:00
Stian Thorgersen e1f2d296bf Merge pull request #363 from stianst/master
Removed org.json
2014-05-06 13:05:30 +01:00
Stian Thorgersen 1d760388de KEYCLOAK-441 Remove org.json 2014-05-06 12:32:29 +01:00
Stian Thorgersen 8286d1ce34 Fix breadcrumb for audit pages£ 2014-05-06 12:26:17 +01:00
Matthias Wessendorf 7b14d29dee Removing JSP code, adding simple text for a simple demo... 2014-05-06 11:29:08 +02:00
Bill Burke 75224d25b0 remove picketlink dependencies 2014-05-05 18:50:41 -04:00
Bill Burke 3a3ae742a4 Merge pull request #360 from patriot1burke/master
adatper deployment context fixes, picketlink abstraction
2014-05-05 18:21:45 -04:00
Bill Burke e7c64b7812 adatper deployment context fixes, picketlink abstraction 2014-05-05 18:20:52 -04:00
Bill Burke e55eb73fb9 Merge pull request #359 from patriot1burke/master
fix tomcat7 build
2014-05-02 13:00:32 -04:00
Bill Burke 201b35be54 fix tomcat7 build 2014-05-02 13:00:12 -04:00
Bill Burke e25b2b05ae Merge pull request #352 from ungarida/master
Tomcat7 adapter
2014-05-02 12:36:06 -04:00
Bill Burke 6b470f826e Merge pull request #358 from patriot1burke/master
dynamic key lookup, relative uris
2014-05-02 12:34:18 -04:00
Bill Burke 4517d9d707 Merge remote-tracking branch 'upstream/master' 2014-05-02 12:30:31 -04:00
Bill Burke 7ff2c77a82 relative uri tests and fixes 2014-05-02 12:30:08 -04:00
Bill Burke e5e43173bc relative redirect uris 2014-05-02 10:58:29 -04:00
Bill Burke 6f3822e6ea relative adapter urls 2014-05-02 10:17:40 -04:00
Bill Burke 95bafc416d Merge pull request #357 from patriot1burke/master
fix mime type
2014-04-30 14:26:12 -04:00
Bill Burke 2576975988 fix mimetype 2014-04-30 14:25:23 -04:00
Bill Burke 8c5ae9d052 fix mimetype 2014-04-30 14:24:37 -04:00
Stian Thorgersen 8ed5282da5 Merge pull request #356 from stianst/master
Documentation
2014-04-30 14:38:27 +01:00
Stian Thorgersen 9b6d9fda43 Updated documentation for js adapter 2014-04-30 14:38:02 +01:00
Stian Thorgersen 77c6b391c4 Document how to enable social only login 2014-04-30 14:07:03 +01:00
Stian Thorgersen c8432f74f6 Merge pull request #355 from stianst/master
KEYCLOAK-211 Enable social only login
2014-04-30 13:36:25 +01:00
Stian Thorgersen 8fa3425886 KEYCLOAK-211 Enable social only login 2014-04-30 11:57:44 +01:00
Stian Thorgersen 84396d0da1 Merge pull request #354 from stianst/master
KEYCLOAK-333 Removed use of RestEasy's Logger
2014-04-30 11:06:51 +01:00
Stian Thorgersen 38e6bde07e KEYCLOAK-415 Make sure query params are included when navigating in acct mngmt 2014-04-30 11:03:13 +01:00
Stian Thorgersen 9811aaeecc KEYCLOAK-333 Removed use of RestEasy's Logger 2014-04-30 10:13:53 +01:00
Stian Thorgersen 7284aae4df Merge pull request #353 from stianst/constraints
Ensure role name unique within realm/app
2014-04-30 09:50:58 +01:00
Stian Thorgersen 316431c4d1 Ensure role name unique within realm/app 2014-04-30 09:50:29 +01:00
Stian Thorgersen bb039576d4 Merge pull request #351 from stianst/constraints
Ensure application and client names are unique within realm
2014-04-29 17:16:25 +01:00
Stian Thorgersen 646e762cbe Ensure user loginName and email unique within realm 2014-04-29 17:15:28 +01:00
Stian Thorgersen 1d94649b96 Ensure application and client names are unique within realm 2014-04-29 12:03:09 +01:00
Stian Thorgersen 1fa019e9dd Merge pull request #350 from stianst/constraints
Enforce that realm name is unique in model
2014-04-29 10:44:42 +01:00
Stian Thorgersen 364998b34b Enforce that realm name is unique in model 2014-04-29 10:43:27 +01:00
Stian Thorgersen 6778fbfa3b Merge pull request #349 from mposolda/master
Fix testsuite with mongo. Upgrade picketlink and enable connection pooling for LDAP
2014-04-29 10:23:22 +01:00
mposolda bb11ad2ec1 KEYCLOAK-419 Upgrade picketlink dependency and enable ldap connection pooling 2014-04-29 11:10:10 +02:00
mposolda b6386630fb Fix testsuite with mongo 2014-04-29 10:37:43 +02:00
Bill Burke 363e5cdf64 Merge pull request #348 from patriot1burke/master
KeycloakApplication refactor
2014-04-28 18:05:59 -04:00
Bill Burke 7594be8014 merge 2014-04-28 18:05:03 -04:00
Bill Burke 62f8811a46 Merge remote-tracking branch 'upstream/master'
Conflicts:
	bundled-war-example/src/main/java/org/keycloak/server/KeycloakServerApplication.java
	bundled-war-example/src/main/webapp/WEB-INF/web.xml
2014-04-28 18:04:50 -04:00
Bill Burke 2d1dc4a874 application refactor 2014-04-28 17:34:06 -04:00
Stian Thorgersen 0546aebc84 More fixes to bundled war example 2014-04-28 14:32:25 +01:00
Stian Thorgersen c06009d5fb KEYCLOAK-430 Fix bundled war example 2014-04-28 13:36:34 +01:00
Stian Thorgersen 3d02fd5d0e KEYCLOAK-429 Remove role from composite roles when deleted 2014-04-28 12:55:04 +01:00
Bill Burke 562f6703b6 Merge remote-tracking branch 'upstream/master' 2014-04-25 13:15:00 -04:00
Stian Thorgersen 573c88564b Temporarily disabled unique constraint on Realm names as it breakes tests 2014-04-25 17:58:49 +01:00
Stian Thorgersen dabf101d96 Merge pull request #347 from stianst/master
Ensure Realm names are unique
2014-04-25 17:02:29 +01:00
Stian Thorgersen 292089cee8 Ensure Realm names are unique 2014-04-25 17:02:03 +01:00
Bill Burke 5594c73ceb Merge remote-tracking branch 'upstream/master' 2014-04-25 10:34:35 -04:00
Stian Thorgersen ce6652efb1 Merge pull request #346 from stianst/master
KEYCLOAK-347 Link realm and realm-permissions application
2014-04-25 15:32:47 +01:00
Stian Thorgersen f64f55a4c6 KEYCLOAK-347 Link realm and realm-permissions application 2014-04-25 15:32:28 +01:00
Bill Burke 9a3a8c39f4 merge 2014-04-25 10:03:56 -04:00
Bill Burke 1de4c31750 wtf 2014-04-25 10:02:49 -04:00
Bill Burke bda85f700a Merge pull request #343 from matzew/master
Adding simple CDI injection into resources
2014-04-25 09:58:59 -04:00
Stian Thorgersen 3471ce6c63 Merge pull request #345 from stianst/master
KEYCLOAK-385 Add CORS support to refresh token
2014-04-25 14:09:56 +01:00
Stian Thorgersen b8dfac391a KEYCLOAK-385 Add CORS support to refresh token 2014-04-25 14:09:39 +01:00
Stian Thorgersen 28546ad031 Merge pull request #344 from stianst/master
Theme support for admin
2014-04-25 13:59:41 +01:00
Stian Thorgersen d6e5e376bf Theme support for admin 2014-04-25 13:58:58 +01:00
Matthias Wessendorf 9b055c6ad6 Adding simple CDI injection into resources 2014-04-25 13:24:21 +02:00
Stian Thorgersen a3c17d1071 Merge pull request #342 from stianst/master
Added support for common resources to themes
2014-04-24 09:27:02 +01:00
Stian Thorgersen d57cd1aa91 Added support for common resources to themes 2014-04-24 09:26:19 +01:00
Bill Burke 3405330d92 Merge pull request #341 from patriot1burke/master
brute force fixes
2014-04-23 17:56:34 -04:00
Bill Burke 541e865fe8 brute force fixes 2014-04-23 17:55:13 -04:00
Davide Ungari c03b97d2d4 #cfg update version 2014-04-20 12:06:31 +02:00
Davide Ungari 5d275de8b2 #dev integrated adapter tomcat7 2014-04-18 08:32:42 +02:00
Bill Burke 80f3a3152c Merge pull request #340 from patriot1burke/master
brute force settings
2014-04-15 11:37:49 -04:00
Bill Burke 54abfb670c brute force settings 2014-04-15 11:37:27 -04:00
Bill Burke b41bf9f231 Merge pull request #339 from patriot1burke/master
more brute force detection
2014-04-14 18:59:09 -04:00
Bill Burke 2b8d2288fb more brute force detection 2014-04-14 18:58:45 -04:00
Bill Burke b556ff8a83 Merge pull request #338 from patriot1burke/master
move things around
2014-04-14 16:53:51 -04:00
Bill Burke 4c8471ef43 move things around 2014-04-14 16:51:53 -04:00
Bill Burke 1aa58ec580 Merge pull request #337 from patriot1burke/master
app/keycloak self bootstrapping bundle
2014-04-11 13:26:22 -04:00
Bill Burke 272806a3f3 app/keycloak self bootstrapping bundle 2014-04-11 13:24:07 -04:00
Bill Burke 51a718f337 Merge pull request #336 from patriot1burke/master
workarounds for resteasy 2.3.7 bugs
2014-04-10 22:34:19 -04:00
Bill Burke cc982cf246 workarounds for resteasy 2.3.7 bugs 2014-04-11 10:33:53 -04:00
Bill Burke 0896808625 Merge pull request #334 from mposolda/master
Reenable AuthProvidersIntegrationTest.registerUserLdapSuccess. Add varia...
2014-04-10 12:16:08 -04:00
Bill Burke c562e88e75 Merge pull request #335 from patriot1burke/master
fix server jboss-deployment-structure
2014-04-10 12:15:57 -04:00
Bill Burke abd7163205 fix server jboss-deployment-structure 2014-04-11 00:15:35 -04:00
mposolda 1701afb6d4 Fix standalone KeycloakServer 2014-04-10 14:59:54 +02:00
mposolda d8937b11cf Reenable AuthProvidersIntegrationTest.registerUserLdapSuccess. Add variable sleep after undertow stop in testsuite (some windows envs have issues when server is not fully stopped) 2014-04-10 10:29:13 +02:00
Bill Burke de871585e0 Merge pull request #333 from patriot1burke/master
remove transitive dependencies
2014-04-09 23:30:16 -04:00
Bill Burke b41c45d40a remove transitive dependencies 2014-04-10 11:29:47 -04:00
Bill Burke ecaa0d1401 use resteasy 2.3.7 2014-04-10 05:21:07 -04:00
Bill Burke cdc35d99bc cleanup 2014-04-09 22:34:36 -04:00
Bill Burke f5f96c5021 Merge pull request #332 from patriot1burke/master
use resteasy 2.3.7
2014-04-09 17:21:37 -04:00
Bill Burke 29a139f56b Merge pull request #331 from patriot1burke/master
cleanup
2014-04-09 14:00:19 -04:00
Stian Thorgersen daf1f2ed45 Merge pull request #330 from mposolda/master
Fix LDAP test on windows. Ensure that directory with data for embedded A...
2014-04-09 18:44:57 +01:00
mposolda eb37890924 Fix LDAP test on windows. Ensure that directory with data for embedded ApacheDS is deleted 2014-04-09 18:56:36 +02:00
Stian Thorgersen eb3e69d5d7 Updated audit timer 2014-04-09 06:40:15 +01:00
mposolda 5aefe52ccc Refactoring of Authentication SPI and Picketlink to use ProviderSessions
Refactoring of ProviderSessionFactory to support dependencies between components
Calling lifecycle methods
Removing KeycloakRegistry
2014-04-09 00:34:01 +02:00
mposolda 0d00db281a Fix the case when user is directly deleted in LDAP server 2014-04-09 00:06:04 +02:00
mposolda c8c4cfbaae Restructure of authentication module and packages 2014-04-09 00:06:04 +02:00
Bill Burke d5be56fa21 Merge pull request #328 from patriot1burke/master
fix broken build
2014-04-08 11:33:38 -04:00
Bill Burke 1d55a4f1e9 fix broken build 2014-04-08 23:32:26 -04:00
Stian Thorgersen 83bc115b63 Merge pull request #327 from stianst/master
Add timer jars to server
2014-04-08 13:09:26 +01:00
Stian Thorgersen e952ff142d Add timer jars to server 2014-04-08 13:09:06 +01:00
Stian Thorgersen b94bad7f58 Merge pull request #326 from stianst/master
Clearing audit events through admin console and through timer
2014-04-08 12:43:52 +01:00
Stian Thorgersen 354cf02aaa Fix audit mongo tests 2014-04-08 12:43:01 +01:00
Stian Thorgersen 7f0cf3eda8 Allow clearing audit events through admin console, and added timer to clear expired events 2014-04-08 11:32:20 +01:00
Stian Thorgersen 9bc0bd452d Merge pull request #325 from mposolda/master
Logging message during ldap bootstrap
2014-04-08 09:43:05 +01:00
mposolda d124f43423 Logging message during ldap bootstrap 2014-04-07 19:40:02 +02:00
Stian Thorgersen a197bda363 Merge pull request #324 from stianst/master
Audit configurable through admin console
2014-04-07 18:06:43 +01:00
Stian Thorgersen 8ca46fa35d Audit configurable through admin console 2014-04-07 17:58:52 +01:00
Stian Thorgersen aec8feab49 Merge pull request #323 from stianst/master
Move setting cors properties
2014-04-07 13:59:39 +01:00
Stian Thorgersen e7c30fff48 Move setting cors properties 2014-04-07 13:58:35 +01:00
Stian Thorgersen 2916cce6fb Merge pull request #322 from stianst/master
KEYCLOAK-411 Set cors properties in KeycloakDeployment
2014-04-07 13:39:53 +01:00
Stian Thorgersen bf95c2ba85 KEYCLOAK-411 Set cors properties in KeycloakDeployment 2014-04-07 13:39:32 +01:00
Stian Thorgersen 6081572179 Merge pull request #321 from stianst/master
Fix testsuite for mongo
2014-04-07 13:38:01 +01:00
Stian Thorgersen bbf5fee320 Fix testsuite for mongo 2014-04-07 13:35:26 +01:00
Stian Thorgersen d66862fd26 Merge pull request #320 from matzew/master
Adding JPA-Audit PU
2014-04-07 13:21:26 +01:00
Matthias Wessendorf 1c0ef4621e Adding JPA-Audit PU 2014-04-07 14:01:31 +02:00
Stian Thorgersen 323e032287 Merge pull request #319 from stianst/master
Update admin console to allow changing context-path for KC
2014-04-07 11:21:48 +01:00
Stian Thorgersen 948960f33f Allow changing base url for Keycloak 2014-04-07 10:54:27 +01:00
Stian Thorgersen abde247e14 Moved admin-ui-styles into admin-ui 2014-04-07 10:37:21 +01:00
Stian Thorgersen a0cd2bf5a7 Merge pull request #318 from mposolda/ldap
Basic support for registration of new users in AuthenticationProvider (like LDAP). Other improvements in auth SPI
2014-04-07 09:35:35 +01:00
mposolda 58083fbb96 Hide password tab in acct management if passwordUpdate not supported 2014-04-04 23:47:12 +02:00
mposolda ad068a300b Basic support for registration of new users in AuthenticationProvider 2014-04-04 19:53:02 +02:00
mposolda be320e11a5 Improve handling of exceptions thrown from picketlink 2014-04-04 16:35:26 +02:00
Stian Thorgersen b7826be103 Merged humanfriendly filter with capitalize 2014-04-04 15:30:06 +01:00
Stian Thorgersen 216e24864a Merge branch 'ldap' of https://github.com/mposolda/keycloak into mposolda-ldap
Conflicts:
	admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
	admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
2014-04-04 15:29:03 +01:00
Stian Thorgersen afea032322 Merge pull request #317 from stianst/master
Added audit to admin console
2014-04-04 15:19:40 +01:00
Stian Thorgersen 88ddc8ebca Added audit to admin console 2014-04-04 15:16:08 +01:00
mposolda 2aac603fad Added configuration of authentication providers into admin console 2014-04-04 13:20:12 +02:00
Stian Thorgersen 8caf3fa83a Added details to log view in acct mngmt 2014-04-04 11:01:18 +01:00
Stian Thorgersen 42975f0edf Merge pull request #315 from stianst/master
Added audit log to account mngmt
2014-04-03 18:24:11 +01:00
Stian Thorgersen 3433227fa7 Added audit log to account mngmt 2014-04-03 16:27:31 +01:00
Stian Thorgersen e6067c915d Added ProviderSession to requests 2014-04-03 16:27:31 +01:00
Stian Thorgersen fcfbf156ce Merge pull request #314 from mposolda/ldap
Fixing testsuite failures with mongo model
2014-04-03 10:26:54 +01:00
mposolda d564955cab Fixing testsuite failures with mongo model 2014-04-03 10:58:39 +02:00
Bill Burke 30d523a5c2 Merge pull request #313 from patriot1burke/master
brute force protection
2014-04-02 20:27:15 -04:00
Bill Burke 126b444d77 brute force merge 2014-04-02 20:26:33 -04:00
Bill Burke d58870545f brute force protection 2014-04-02 20:09:14 -04:00
Bill Burke 231e8895a5 Merge pull request #309 from vibe13/master
Cleaning of pom.xml files
2014-04-02 11:39:04 -04:00
Bill Burke 7d7b9e5b73 Merge pull request #312 from mposolda/ldap
Refactoring of AuthenticationProvider SPI. Add Ldap settings into admin console
2014-04-02 11:37:11 -04:00
mposolda 25bf6d63b4 Refactoring of AuthenticationProvider SPI 2014-04-02 17:27:07 +02:00
mposolda 05cd8a82e5 Storing default AuthenticationProvider at the realm creation time 2014-04-02 17:17:33 +02:00
mposolda 1c218c8e9b Add Ldap settings into admin console 2014-04-02 17:17:32 +02:00
Stian Thorgersen 8af81cd290 Merge pull request #311 from stianst/master
Added JPA and Mongo audit providers. Add audit deps to server war.
2014-04-02 14:05:07 +01:00
Stian Thorgersen 869601d041 Added audit deps to server war 2014-04-02 11:37:22 +01:00
Stian Thorgersen 21239fbab9 Added Mongo audit provider 2014-04-01 18:00:35 +01:00
Stian Thorgersen 722a077de7 Added JPA audit provider£ 2014-04-01 15:13:53 +01:00
Stian Thorgersen 1e15e97d17 Merge pull request #310 from stianst/audit
Started audit
2014-03-31 17:43:42 +01:00
Stian Thorgersen 225307e855 KEYCLOAK-389 Added AuditListener SPI
KEYCLOAK-390 Added JBoss Logging AuditListener
KEYCLOAK-391 Audit Token events
2014-03-31 17:35:14 +01:00
Andrea Vibelli 84f1e1b56e Extraction into properties of maven-compiler-plugin source and target 2014-03-31 11:17:16 +02:00
Andrea Vibelli bdf1c1f495 Remove unnecessary version declaration and insertion of groupId 2014-03-31 10:45:11 +02:00
Andrea Vibelli 5e1da96177 Remove unnecessary version declarations 2014-03-31 10:44:59 +02:00
Andrea Vibelli 3ddb2084cd Remove duplicated dead code 2014-03-31 10:44:47 +02:00
Andrea Vibelli d831e25366 Remove unnecessary groupId declarations 2014-03-31 10:44:34 +02:00
Andrea Vibelli 37fdc50fb4 Remove warnings about no schema definition in pom.xml files 2014-03-31 10:44:22 +02:00
Bill Burke d8a3025ea1 Merge remote-tracking branch 'upstream/master' 2014-03-27 14:10:01 -04:00
Bill Burke 911cf2ae45 Merge pull request #307 from jpkrohling/JavascriptCharset
Fixed charset issue while decoding token with atob
2014-03-27 11:40:48 -04:00
Bill Burke 60ba41e82f Merge pull request #308 from mposolda/ldap
Fix LDAP tests and teststuite on windows. Set initial password directly ...
2014-03-27 11:40:28 -04:00
mposolda 129eb6a3be Fix LDAP tests and teststuite on windows. Set initial password directly in code as import from LDIF is problematic on windows 2014-03-27 16:20:03 +01:00
Juraci Paixão Kröhling 34fbdf4358 Fixed charset issue while decoding token with atob 2014-03-27 09:48:16 +01:00
Bill Burke 51b3de7383 unfix uribuilder 2014-03-26 10:19:56 -04:00
Bill Burke 0ec19c9ec9 uribuilder fix 2014-03-26 10:09:41 -04:00
Bill Burke 4711dd083f Merge pull request #306 from mposolda/master
Remove unfinished ldap-settings tab, which I've added accidentally in pr...
2014-03-26 09:42:57 -04:00
mposolda bcc0bcd219 Remove unfinished ldap-settings tab, which I've added accidentally in previous PR 2014-03-26 14:38:11 +01:00
Stian Thorgersen 00aaefb926 Merge pull request #305 from mposolda/master
Added Authentication SPI dependencies to auth-server.war
2014-03-26 12:45:51 +00:00
mposolda 3302c54c43 Added Authentication SPI dependencies to auth-server.war 2014-03-26 12:57:11 +01:00
Stian Thorgersen 434dec2318 Merge pull request #304 from mposolda/ldap
KEYCLOAK-388 - Auth SPI should be able to differentiate between the INVA...
2014-03-26 11:55:34 +00:00
mposolda 96a05a9b77 Console: work in progress 2014-03-26 12:25:46 +01:00
mposolda 77a44751a6 KEYCLOAK-388 - Auth SPI should be able to differentiate between the INVALID_USERNAME and INVALID_CREDENTIALS 2014-03-26 09:49:47 +01:00
Stian Thorgersen 51b95b1b91 Merge pull request #303 from mposolda/ldap
Fixes in AuthenticationProvider. Fixing testsuite
2014-03-25 14:17:31 +00:00
mposolda ab02dea902 Fixes in AuthenticationProvider. Fixing testsuite 2014-03-25 15:05:08 +01:00
Bill Burke 28a1bd8535 Merge pull request #302 from mposolda/ldap
Authentication SPI. Implementations based on Picketlink+LDAP, model and ...
2014-03-25 08:42:27 -04:00
Stian Thorgersen a2a8e72e20 Merge pull request #301 from stianst/master
Fixes to JS sdk
2014-03-25 10:37:48 +00:00
Stian Thorgersen 4788dd8bd2 Fixes to JS sdk 2014-03-25 10:37:20 +00:00
mposolda 793f69d4b6 Authentication SPI. Implementations based on Picketlink+LDAP, model and external model (other realm). Added KeycloakRegistry 2014-03-24 23:11:11 +01:00
Stian Thorgersen 0879013a90 Merge pull request #300 from corinnekrych/readme.instructions.improvement
fix typos and formatting
2014-03-24 09:36:35 +00:00
corinnekrych 877a552f3b fix typos and formatting 2014-03-21 17:34:24 +01:00
Stian Thorgersen 2403e1f465 Merge pull request #299 from stianst/master
Added support for Cordova to keycloak.js
2014-03-19 17:02:05 +00:00
Stian Thorgersen 119423895d Removed files that shouldn't have been committed 2014-03-19 16:54:20 +00:00
Stian Thorgersen 702ae0307e Fixes to account referrer to allow configurable referrer uri based on app redirect uri 2014-03-19 16:52:41 +00:00
Stian Thorgersen b5bdeae2a2 Updates to keycloak.js, including adding support for Cordova
Conflicts:
	examples/cordova/example-realm.json
	examples/js-console/src/main/webapp/index.html
	integration/js/src/main/resources/META-INF/resources/js/keycloak.js
2014-03-19 16:52:41 +00:00
Stian Thorgersen 4159a97881 Merge pull request #298 from stianst/master
Updates to keycloak.js
2014-03-18 14:44:42 +00:00
Stian Thorgersen 2d5e6520db Updates to keycloak.js 2014-03-18 14:43:48 +00:00
Stian Thorgersen 331ab71427 KEYCLOAK-380 Added OAuth2Constants 2014-03-15 10:43:52 +00:00
Stian Thorgersen f9aaa16cfe KEYCLOAK-378 KEYCLOAK-379 KEYCLOAK-381 Fix refresh token if token contains app roles. Changed long time fields in AccessCode and AccessToken to int 2014-03-15 10:15:10 +00:00
Stian Thorgersen 2ebc32793a Started installed application adapter 2014-03-14 17:01:08 +00:00
Bill Burke cec948ffb4 Merge pull request #297 from patriot1burke/master
further adapter tests
2014-03-14 11:21:53 -04:00
Bill Burke 466b6762d9 further adapter tests 2014-03-14 11:21:32 -04:00
Bill Burke e03737c665 Merge pull request #296 from patriot1burke/master
adapter testing
2014-03-13 21:48:14 -04:00
Bill Burke 3f1b7a0335 adapter testing 2014-03-13 21:47:46 -04:00
Bill Burke f26f2faa72 Merge pull request #295 from patriot1burke/master
bump version to beta-1 snapshot
2014-03-13 11:25:01 -04:00
Bill Burke 7763da1273 bump version to beta-1 snapshot 2014-03-13 11:24:38 -04:00
Bill Burke bb6791d6eb Merge pull request #294 from patriot1burke/master
bump version
2014-03-13 10:58:48 -04:00
Bill Burke 1c6b867079 bump version 2014-03-13 10:58:22 -04:00
Bill Burke 6b57684c52 Merge pull request #293 from patriot1burke/master
NPE in create user
2014-03-13 10:57:13 -04:00
Bill Burke 08769a2daa NPE in create user 2014-03-13 10:56:09 -04:00
Stian Thorgersen 7fff2588b2 Merge pull request #292 from stianst/master
Set next version
2014-03-13 09:12:18 +00:00
Stian Thorgersen ee5cab04e0 Set next version 2014-03-13 09:10:42 +00:00
Bill Burke 9f429e4150 Merge pull request #291 from patriot1burke/master
bump
2014-03-12 17:07:24 -04:00
Bill Burke 9528dbc4dc realm name in OTP 2014-03-12 17:05:57 -04:00
Bill Burke 3ff8d0fd1f bump version 2014-03-12 16:48:12 -04:00
Bill Burke 7b64d9975e Merge pull request #286 from secondsun/KEYCLOAK-355
Fixing java 8 build
2014-03-12 16:43:03 -04:00
Bill Burke ba60a0c04b Merge pull request #289 from stianst/master
Added a README.md in the root of examples to describe the difference bet...
2014-03-12 16:42:48 -04:00
Bill Burke 6926f02cc3 Merge pull request #290 from patriot1burke/master
buncha stuff
2014-03-12 15:58:22 -04:00
Bill Burke 52018b1f81 revoke on logoutAll 2014-03-12 15:57:43 -04:00
Bill Burke fcc95ef99b fix delete realm 2014-03-12 15:00:31 -04:00
Stian Thorgersen 2f29fab931 Added a README.md in the root of examples to describe the difference between preconfigured-demo and unconfigured-demo 2014-03-12 17:15:54 +00:00
Stian Thorgersen 0c2767afd7 Minor updates to docs 2014-03-12 17:15:04 +00:00
Stian Thorgersen 4a90133d29 Added customer-portal-js and customer-portal-cli to example testrealm.js and added instructions for customer-portal-cli to README.md 2014-03-12 17:14:40 +00:00
Stian Thorgersen 3c40e9694f Merge pull request #287 from stianst/master
Added subject in token changed to user id to migration doc
2014-03-12 15:59:27 +00:00
Stian Thorgersen 30358a7582 Added subject in token changed to user id to migration doc 2014-03-12 15:59:04 +00:00
Summers Pittman 20880ffd71 Fixing java 8 build 2014-03-12 10:40:47 -04:00
Bill Burke e48cc006f6 Merge remote-tracking branch 'upstream/master' 2014-03-12 10:17:24 -04:00
Bill Burke accb56be7f user import fix 2014-03-12 10:17:09 -04:00
Stian Thorgersen a1ea79e382 Revert changes to lib until it works on 7 and 8 2014-03-12 14:11:05 +00:00
Stian Thorgersen a1f10ef54b Merge pull request #285 from stianst/master
Add user friendly username for social links. Show access denied if social login is cancelled
2014-03-12 13:52:28 +00:00
Stian Thorgersen fbc39e82a1 KEYCLOAK-365 Add IDToken properties to JS SDK 2014-03-12 11:31:59 +00:00
Stian Thorgersen 0214827492 KEYCLOAK-364 Show access denied if social login is cancelled 2014-03-12 09:21:11 +00:00
Stian Thorgersen 6dc156712e KEYCLOAK-359 Add user friendly username for social links 2014-03-12 08:52:47 +00:00
Bill Burke 53f671e8af Merge pull request #283 from patriot1burke/master
adapter security context propagation
2014-03-11 18:08:23 -04:00
Bill Burke a15e7d1f6e add security domain docs 2014-03-11 17:57:35 -04:00
Bill Burke e836371887 security context propagation 2014-03-11 17:40:53 -04:00
Viliam Rockai 53b596d435 KEYCLOAK-336 removed non-working pagination from role list 2014-03-11 17:54:20 +00:00
Viliam Rockai 0dbd4417af KEYCLOAK-360 fixed account template menu 2014-03-11 18:24:10 +01:00
Viliam Rockai d1b1a98a0a KEYCLOAK-356 added autofocus to the new passwd field 2014-03-11 18:24:10 +01:00
Viliam Rockai ccc4f5a114 KEYCLOAK-362 - fixing the select2 with req user actions 2014-03-11 18:24:10 +01:00
Stian Thorgersen 03a0cce119 Merge pull request #281 from stianst/master
KEYCLOAK-357 KEYCLOAK-358 Set enabled default to true when creating appl...
2014-03-11 13:53:42 +00:00
Stian Thorgersen 26b3ec0b2a KEYCLOAK-357 KEYCLOAK-358 Set enabled default to true when creating applications, clients and users 2014-03-11 13:53:15 +00:00
Stian Thorgersen 7992509fde Merge pull request #280 from stianst/master
KEYCLOAK-355 Fix distribution build on JDK8
2014-03-11 09:52:52 +00:00
Stian Thorgersen b208cae333 KEYCLOAK-355 Fix distribution build on JDK8 2014-03-11 09:52:22 +00:00
Bill Burke 9749c65065 Merge pull request #278 from cardosogabriel/master
Adjustments on headers (top bar)
2014-03-10 20:08:14 -04:00
Bill Burke 9109642821 Merge pull request #279 from patriot1burke/master
refactor adapters complete
2014-03-10 20:08:06 -04:00
Gabriel Cardoso b6503748ca Adjusting headers to make them horizontally aligned to the content 2014-03-10 20:59:57 -03:00
Bill Burke 0284d040dc refactor adapters complete 2014-03-10 18:52:57 -04:00
Stian Thorgersen d8c13c9ac2 Merge pull request #277 from stianst/master
Remove clientSecret required in JS lib, and removed storing token to ses...
2014-03-10 17:28:38 +00:00
Stian Thorgersen be7008f253 Remove clientSecret required in JS lib, and removed storing token to sessionStore 2014-03-10 17:27:05 +00:00
Stian Thorgersen 63bc2d6dbb Merge pull request #276 from mposolda/account-social
KEYCLOAK-26 Linking social providers to existing account
2014-03-10 11:43:01 +00:00
mposolda 3d0d130622 KEYCLOAK-26 Linking social providers to existing account 2014-03-10 11:31:59 +01:00
Bill Burke 8c3225914c Merge pull request #273 from cardosogabriel/master
Improvements in the console header, content area background, favicon and logos
2014-03-07 18:48:45 -05:00
Bill Burke 798e3ce86b Merge pull request #274 from patriot1burke/master
adapter refactoring
2014-03-07 18:48:31 -05:00
Bill Burke 49c79f156a adapter refactoring 2014-03-07 18:48:02 -05:00
Gabriel Cardoso d3634c1180 Updated FavIcon and Keycloak Logos
- more attractive favicon
- updated keycloak logos (bigger distance between the symbol and text)
2014-03-07 18:51:10 -03:00
Gabriel Cardoso c345ee9e5a Fixing Content Area Background
- Now there is no vertical scroll when the content does not occupy the
full height;
- The background area / borders go until the bottom for longer pages
2014-03-07 15:05:22 -03:00
Gabriel Cardoso 12f080e803 Full width header 2014-03-07 14:49:55 -03:00
Bill Burke 2b01de5ecf remove transaction 2014-03-07 08:35:41 -05:00
Stian Thorgersen 61102845ed Merge pull request #271 from mposolda/mongo
Fix mongo to pass with testsuite. Other improvements
2014-03-07 13:01:26 +00:00
Stian Thorgersen ca3b28fda7 Merge pull request #272 from stianst/master
Added docs for installed applications
2014-03-07 13:00:59 +00:00
Stian Thorgersen 854158a9c2 Added docs for installed applications 2014-03-07 13:00:20 +00:00
mposolda 11bcd84738 Added MongoClientProvider. Possibility to configure mongo from external source 2014-03-07 13:39:43 +01:00
mposolda da3f1a21a2 Fix testsuite with Mongo. Added ClientAdapter and ClientEntity to mongo model to improve inheritance 2014-03-07 12:24:41 +01:00
Stian Thorgersen 2edb93ef54 Merge pull request #270 from stianst/master
Added documentation for admin access control
2014-03-07 09:11:00 +00:00
Stian Thorgersen 0f0353674d Added documentation for admin access control 2014-03-07 09:10:16 +00:00
Bill Burke 62b0789e5f Merge pull request #269 from patriot1burke/master
refactor adapters
2014-03-06 21:16:30 -05:00
Bill Burke 26d0f993ba formating for oauth client 2014-03-06 21:16:07 -05:00
Bill Burke a653e5a406 refactor adapters 2014-03-06 21:13:55 -05:00
Bill Burke 0fd0240be7 Merge pull request #268 from cardosogabriel/master
Adjusting font-sizes, margins and a few design details
2014-03-06 15:15:27 -05:00
Gabriel Cardoso 3ea9df89a9 Adjusting font-sizes, margins and a few design details 2014-03-06 16:05:19 -03:00
Bill Burke 3c463419ca Merge pull request #267 from patriot1burke/master
client type selection, remove scope param support
2014-03-06 10:27:10 -05:00
Bill Burke dd292c1d52 remove scope support for now part 2 2014-03-06 10:26:46 -05:00
Bill Burke 801ef3281a remove scope support for now 2014-03-06 09:59:27 -05:00
Bill Burke 8f29bf0a5a client type selection 2014-03-06 09:48:52 -05:00
Stian Thorgersen 6f752e68c8 Merge pull request #266 from stianst/master
Added basic command-line example
2014-03-06 12:09:22 +00:00
Stian Thorgersen 12978a4343 Added basic command-line example 2014-03-06 12:08:44 +00:00
Bill Burke b543e22eee Merge pull request #265 from patriot1burke/master
public clients
2014-03-05 20:26:52 -05:00
Bill Burke 599faa3cb5 public clients 2014-03-05 20:26:27 -05:00
Bill Burke d7b616773a Merge pull request #264 from patriot1burke/master
session mgmt as7
2014-03-05 15:00:37 -05:00
Bill Burke 633ddb70e7 session mgmt as7 2014-03-05 15:00:21 -05:00
Stian Thorgersen d0a6a8ae3b Merge pull request #263 from stianst/master
Added support for any port with http://localhost redirect uri. Added Config to centralize system properties used to configure KC
2014-03-05 15:23:29 +00:00
Stian Thorgersen 60bb05e6ca Added Config to centralize system properties used to configure KC 2014-03-05 15:20:53 +00:00
Stian Thorgersen 0219aa1e4c Added support for any port with http://localhost redirect uri 2014-03-05 12:54:57 +00:00
Bill Burke 34260b7428 Merge pull request #262 from patriot1burke/master
session mgmt
2014-03-04 22:25:56 -05:00
Bill Burke d6bd02ea7d session mgmt 2014-03-04 22:25:33 -05:00
Bill Burke 7699dd2701 Merge remote-tracking branch 'upstream/master' 2014-03-04 15:52:40 -05:00
Bill Burke 2d86b29b6c session stats 2014-03-04 15:52:27 -05:00
Stian Thorgersen 67db515fe1 Fix for small height 2014-03-04 15:41:06 +00:00
Stian Thorgersen df61879fb1 Merge pull request #261 from stianst/master
Fixes to cors for js. Improvements to login form for small screens. Fix ng-disabled with kc-read-only forms
2014-03-04 15:28:51 +00:00
Stian Thorgersen 77259320cf Fix js for cors requests 2014-03-04 15:27:13 +00:00
Stian Thorgersen e0dff8104d Fix ng-disabled when used with kc-read-only forms 2014-03-04 15:25:55 +00:00
Stian Thorgersen 9816fdd842 Improvements to login forms for small screen 2014-03-04 15:25:55 +00:00
Bill Burke 9d4cc19e62 Merge pull request #260 from patriot1burke/master
revocation app level
2014-03-03 17:10:34 -05:00
Bill Burke 06288fa07b revocation app level 2014-03-03 17:10:15 -05:00
Bill Burke 735486c36e Merge pull request #259 from patriot1burke/master
revocation and fix login.ftl
2014-03-03 16:32:22 -05:00
Bill Burke d783c6200e fix login.ftl page 2014-03-03 16:30:09 -05:00
Bill Burke e307916233 revocation ui 2014-03-03 16:01:22 -05:00
Bill Burke 01154f18dd Merge remote-tracking branch 'upstream/master' 2014-03-03 15:50:21 -05:00
Bill Burke 716972347d revocation 2014-03-03 15:50:10 -05:00
Stian Thorgersen 4b8c6f338a Merge pull request #258 from stianst/master
Fix social tab, and minor fixes to xs screens
2014-03-03 13:43:51 +00:00
Stian Thorgersen 170afedd19 Minor fixes for login forms on xs screens 2014-03-03 13:43:05 +00:00
Stian Thorgersen 8881ba2d72 Fix social tab not showing 2014-03-03 13:42:40 +00:00
Stian Thorgersen 1cff76922b Merge pull request #257 from stianst/master
Fixes to login forms, and started support for installed applications (for Cordova)
2014-03-03 13:00:18 +00:00
Stian Thorgersen c0f49763f1 Updates to login form styles 2014-03-03 12:58:57 +00:00
Stian Thorgersen 87aaaf0b06 Started support for installed applications 2014-03-03 12:58:16 +00:00
Bill Burke ec309e7e02 Merge pull request #256 from patriot1burke/master
refactor getRoleById, fix bugs
2014-03-02 20:41:48 -05:00
Bill Burke 22b25a0d9e KEYCLOAK-341 2014-03-02 20:40:34 -05:00
Bill Burke 8126110312 refactor getRoleById 2014-03-02 20:28:58 -05:00
Bill Burke f8eb56d6b7 Merge remote-tracking branch 'upstream/master' 2014-03-02 18:09:55 -05:00
Bill Burke 21b1ad5e0a remove application sessions tab 2014-03-02 17:56:22 -05:00
Bill Burke 5eddacfee9 Merge pull request #255 from patriot1burke/master
revocation
2014-03-02 17:46:49 -05:00
Bill Burke c8023c6651 revocation next phase: undertow complete 2014-03-02 17:32:25 -05:00
Bill Burke 7b30cc59b8 revocation phase 1 2014-02-28 19:47:05 -05:00
Bill Burke 581d64614e Merge pull request #254 from patriot1burke/master
add claims to grant page
2014-02-28 10:45:54 -05:00
Bill Burke 0d309d058c add claims to grant page 2014-02-28 10:45:12 -05:00
Bill Burke 11559cba50 public realm info update 2014-02-28 09:52:53 -05:00
Bill Burke e02d3748a9 Merge pull request #253 from patriot1burke/master
fix NPE
2014-02-28 09:20:09 -05:00
Bill Burke 236d67f78d Merge remote-tracking branch 'upstream/master' 2014-02-28 09:19:45 -05:00
Bill Burke 4dc4c56921 fix NPE 2014-02-28 09:19:26 -05:00
Stian Thorgersen cec3df71ca Merge pull request #252 from stianst/master
Fix form sometimes read-only on refresh
2014-02-28 10:36:30 +00:00
Stian Thorgersen 63aa303673 Fix form sometimes read-only on refresh 2014-02-28 10:35:34 +00:00
Bill Burke 3e68cc601a Merge pull request #250 from vrockai/FORMDIS
AngularJS update and directives refactoring
2014-02-27 16:50:10 -05:00
Bill Burke 918e3d17f6 Merge pull request #251 from patriot1burke/master
refactor out clientmodel.getAgent()
2014-02-27 16:49:30 -05:00
Bill Burke c78d1c90e4 remove clientmodel.getAgent() 2014-02-27 16:48:34 -05:00
Bill Burke 3fc273070e remove clientmodel.agent phase1 2014-02-27 13:55:04 -05:00
Viliam Rockai 7aa7df43aa KEYCLOAK-276 select2 lib update 2014-02-27 18:22:29 +01:00
Viliam Rockai d65926f3b8 KEYCLOAK-340 custom selectors on realm detail replaced with standard one 2014-02-27 17:41:00 +01:00
Viliam Rockai 5881ef3e11 KEYCLOAK-276 AngularJS upgraded to v1.2.13 2014-02-27 17:08:01 +01:00
Bill Burke f8da693fd0 move secret to clientmodel 2014-02-27 10:59:47 -05:00
Viliam Rockai b413636bf5 KEYCLOAK-340 refactoring of on/off switch 2014-02-27 16:33:16 +01:00
Stian Thorgersen a1dca73ad3 Merge pull request #249 from stianst/master
Added create-realm role
2014-02-27 11:08:24 +00:00
Stian Thorgersen 32b7c464c7 Added create-realm role 2014-02-27 11:07:48 +00:00
Stian Thorgersen d5e0e73369 Merge pull request #248 from stianst/master
Fixed permissions not updated when realm is imported
2014-02-27 10:23:51 +00:00
Stian Thorgersen b3375d4279 Fixed permissions not updated when realm is imported 2014-02-27 10:23:27 +00:00
Bill Burke 4f56170b4c Merge pull request #247 from patriot1burke/master
rename scope rep username to client
2014-02-26 22:23:30 -05:00
Bill Burke 42d30a5a70 rename scope rep username to client 2014-02-26 22:23:04 -05:00
Bill Burke 9dbd19f715 Merge pull request #246 from patriot1burke/master
Use ClientModel wherever possible
2014-02-26 22:05:22 -05:00
Bill Burke 48d39bf977 Use ClientModel wherever possible 2014-02-26 22:04:57 -05:00
Bill Burke a8e588216b Merge pull request #245 from patriot1burke/master
IDToken and claims
2014-02-26 19:30:26 -05:00
Bill Burke c02d532001 refactor model 2014-02-26 19:25:42 -05:00
Bill Burke 0f67feb9dd IDToken setup 2014-02-26 19:04:42 -05:00
Bill Burke b2719ff40a claims UI 2014-02-26 16:41:31 -05:00
Bill Burke 8613452f4f Merge remote-tracking branch 'upstream/master' 2014-02-26 15:29:31 -05:00
Bill Burke b249809d2e claims backend 2014-02-26 15:29:17 -05:00
Stian Thorgersen 4ea7808d5f Merge pull request #244 from stianst/master
Added view roles, including updating console to make forms read-only
2014-02-26 17:39:51 +00:00
Stian Thorgersen ae1b98e38f Added view roles, including updating console to make forms read-only 2014-02-26 17:38:42 +00:00
Stian Thorgersen d0ca9a655d Merge pull request #243 from stianst/admin-access
Started directive to make form read-only
2014-02-26 10:56:22 +00:00
Stian Thorgersen 8d5d786452 Started directive to make form read-only 2014-02-26 10:55:40 +00:00
Bill Burke deaf2f9273 Merge pull request #242 from patriot1burke/master
cors docs
2014-02-25 16:23:54 -05:00
Bill Burke db37053f58 cors docs 2014-02-25 16:23:29 -05:00
Stian Thorgersen dd75864749 Merge pull request #241 from stianst/admin-access
Fixes to permissions in admin console
2014-02-25 15:42:08 +00:00
Stian Thorgersen 98629ac6f8 Fixes to permissions in admin console 2014-02-25 15:41:42 +00:00
Stian Thorgersen fa39ec1c98 Merge pull request #240 from stianst/admin-access
KEYCLOAK-292 Fine-grained admin control
2014-02-25 12:59:31 +00:00
Stian Thorgersen 9a73936002 KEYCLOAK-292 Fine-grained admin control 2014-02-25 12:53:36 +00:00
Bill Burke 7f33593931 Merge pull request #239 from patriot1burke/master
token docs
2014-02-24 20:20:51 -05:00
Bill Burke 88fb1ec747 token docs 2014-02-24 20:20:29 -05:00
Bill Burke a3e437f82a Merge pull request #238 from patriot1burke/master
javascript refresh token
2014-02-24 19:59:22 -05:00
Bill Burke 64065a4573 javascript refresh token 2014-02-24 19:58:54 -05:00
Bill Burke 1acfe1502a Merge pull request #237 from patriot1burke/master
remember me
2014-02-24 17:19:53 -05:00
Bill Burke 4b78857b51 as7 adapter refresh token support 2014-02-24 17:16:45 -05:00
Bill Burke 735009cdd7 Merge remote-tracking branch 'upstream/master' 2014-02-24 09:49:01 -05:00
Bill Burke 166e00f2e8 remember me 2014-02-23 11:30:32 -05:00
Bill Burke 3552f9e4aa Merge pull request #236 from patriot1burke/master
central login timeouts, rememberme framework
2014-02-22 21:52:54 -05:00
Bill Burke 01ddafa83a central login timeouts, rememberme framework 2014-02-22 21:52:29 -05:00
Bill Burke 1bfad2e198 Merge pull request #235 from patriot1burke/master
refresh tokens
2014-02-22 20:41:01 -05:00
Bill Burke 3e88cb3b76 logout 2014-02-22 20:40:06 -05:00
Bill Burke 273e706a42 undertow refresh token support 2014-02-22 17:24:04 -05:00
Bill Burke baa0e453b6 Merge remote-tracking branch 'upstream/master' 2014-02-21 17:37:29 -05:00
Bill Burke 898fcf8fd5 Merge pull request #234 from mposolda/master
Fix testsuite with mongo. Fix RealmModel.get**ById(). Added new unit tests
2014-02-21 17:37:06 -05:00
Bill Burke 3b59bd92fc Merge remote-tracking branch 'upstream/master' 2014-02-21 17:37:00 -05:00
Bill Burke 0a9b82a6f4 refresh tokens server side 2014-02-21 17:36:39 -05:00
mposolda 8a4ef40e9e Added new unit tests. Change get***ById() to find just entities from this realm/application 2014-02-21 23:16:06 +01:00
mposolda e85c2c9826 Fix testsuite with Mongo 2014-02-21 23:16:06 +01:00
Bill Burke 15968e9783 Merge pull request #233 from patriot1burke/master
refactor token creation
2014-02-21 12:13:26 -05:00
Bill Burke 97dd7470ce rename tokenLifespan to accessTokenLifespan 2014-02-21 12:12:54 -05:00
Bill Burke e583dc60ae Merge remote-tracking branch 'upstream/master' 2014-02-21 12:02:36 -05:00
Bill Burke 9607acdb6a refactor token creation 2014-02-21 12:02:24 -05:00
Stian Thorgersen 0ee71b09cd Merge pull request #232 from stianst/master
KEYCLOAK-286 Allow login with username or email. KEYCLOAK-287 Remove recover username as we now support login with email
2014-02-21 15:28:39 +00:00
Stian Thorgersen 05bd92d765 KEYCLOAK-286 Allow login with username or email. KEYCLOAK-287 Remove recover username as we now support login with email 2014-02-21 15:25:55 +00:00
Bill Burke cd68462cf3 Merge pull request #231 from patriot1burke/master
rename same classes/attributes
2014-02-21 10:17:02 -05:00
Bill Burke 9442601e42 rename some classes 2014-02-21 10:16:12 -05:00
Bill Burke 4b1fc788cf Merge remote-tracking branch 'upstream/master' 2014-02-21 09:49:08 -05:00
Bill Burke 94d8dbeeee issuedFor has OpenID claim now 2014-02-21 09:48:48 -05:00
Stian Thorgersen 3cb7e7c1bf Merge pull request #230 from stianst/master
KEYCLOAK-284 Subject in token is now user id instead of username
2014-02-21 10:55:25 +00:00
Stian Thorgersen 9ce8e16063 KEYCLOAK-284 Subject in token is now user id instead of username 2014-02-21 09:48:43 +00:00
Stian Thorgersen c2ca1b2731 Merge pull request #227 from matzew/master
Remove outdated references to alpha-1
2014-02-21 09:46:29 +00:00
Stian Thorgersen d47bf103b0 Merge pull request #228 from cardosogabriel/master
Refinements in the logo
2014-02-21 09:46:08 +00:00
Bill Burke dfacae9beb Merge pull request #229 from patriot1burke/master
tokens/access/codes now uses basic auth
2014-02-20 17:20:06 -05:00
Bill Burke 6794166b58 tokens/access/codes now uses basic auth 2014-02-20 17:19:51 -05:00
Gabriel Cardoso 8583d35153 Refinements in the logo 2014-02-20 15:30:21 -03:00
Matthias Wessendorf 2cb37827a8 Remove outdated references to alpha-1 2014-02-20 12:20:40 +01:00
Bill Burke 158eec7999 Merge pull request #226 from patriot1burke/master
alpha 3 version bump
2014-02-19 23:22:53 -05:00
Bill Burke 00043423c2 alpha 3 version bump 2014-02-19 23:22:34 -05:00
1283 changed files with 239920 additions and 85063 deletions

No files matched your search

-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-admin-ui-styles</artifactId>
<name>Keycloak Admin UI Styles</name>
<description />
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,71 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* Clearfix */
.clearfix:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
line-height: 0;
height: 0;
}
.clearfix {
display: inline-block;
}
html[xmlns] .clearfix {
display: block;
}
* html .clearfix {
height: 1%;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="23px" height="14px" viewBox="0 0 23 14" enable-background="new 0 0 23 14" xml:space="preserve">
<polyline fill="none" stroke="#009AD8" stroke-miterlimit="10" points="1,13 7,7 1,1 "/>
</svg>

Before

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22px" height="52px" viewBox="0 0 22 52" enable-background="new 0 0 22 52" xml:space="preserve">
<polygon fill="#828487" points="11,16 6.818,11.818 7.525,11.111 11,14.586 14.475,11.111 15.182,11.818 "/>
<polygon fill="#DBDADA" points="11,42 6.818,37.818 7.525,37.111 11,40.586 14.475,37.111 15.182,37.818 "/>
</svg>

Before

Width:  |  Height:  |  Size: 699 B

File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd ">
</web-fragment>
-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-admin-ui</artifactId>
<name>Keycloak Admin UI</name>
<description />
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,191 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<style>
.mock_view {
padding: 2em 2em;
}
</style>
<meta charset="utf-8">
<title>Keycloak Admin Console</title>
<link rel="icon" href="/auth/admin-ui/img/favicon.ico">
<!-- Frameworks -->
<!--link rel="stylesheet" href="/auth/admin-ui/bootstrap/css/bootstrap.css"-->
<link rel="stylesheet" href="/auth/admin-ui/patternfly/css/patternfly.css">
<!-- Page styles -->
<link rel="stylesheet" href="/auth/admin-ui/css/admin-console.css">
<link href="lib/select2-3.4.1/select2.css" rel="stylesheet">
<script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
<script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
<script src="lib/angular/select2.js" type="text/javascript"></script>
<script src="lib/fileupload/angular-file-upload.min.js"></script> <!--
<script src="/auth/admin-ui/bootstrap-3.0.0-wip/js/dropdown.js"></script>
<script src="/auth/admin-ui/bootstrap-3.0.0-wip/js/tooltip.js"></script>
-->
<script src="/auth/admin-ui/js/bootstrap-tokenfield.js"></script>
<script src="/auth/admin-ui/bootstrap/js/bootstrap.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/realm.js"></script>
<script src="js/controllers/applications.js"></script>
<script src="js/controllers/oauth-clients.js"></script>
<script src="js/controllers/users.js"></script>
<script src="js/loaders.js"></script>
<script src="js/services.js"></script>
<style>
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
</style>
</head>
<body class="admin-console" data-ng-controller="GlobalCtrl" ng-cloak>
<div id="idletimeout">
You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
</div>
<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
<div class="alert alert-{{notification.type}}"><strong>{{notification.header}}</strong> {{notification.message}}</div>
</div>
<header class="navbar navbar-default navbar-pf navbar-main header">
<div class="container">
<nav id="kc-navigation" class="navbar" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<h1 class="navbar-title"><Strong>KeyCloak</Strong> Central Login</h1>
</div>
<div class="collapse navbar-collapse navbar-collapse-1">
<ul class="nav navbar-nav navbar-utility" data-ng-show="auth.loggedIn">
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="pficon pficon-user"></span>
{{auth.user.displayName}}<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/auth/rest/realms/keycloak-admin/account?referrer=admin-console">Manage Account</a></li>
<li class="separator"><a href="/auth/rest/admin/logout">Sign Out</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-primary persistent-secondary" data-ng-controller="RealmDropdownCtrl">
<li class="dropdown context" data-ng-show="showNav()">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Realm: {{current.realm.id}}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li data-ng-repeat="realm in current.realms">
<a href="" ng-click="changeRealm(realm.id)" >{{realm.id}}</a>
</li>
</ul>
</li>
<!--------------------------------------->
<li class="active">
<a href="#">Settings</a>
<ul class="nav navbar-nav navbar-persistent">
<li class="active">
<a href="#">General</a>
</li>
<li>
<a href="#">Roles</a>
</li>
<li>
<a href="#">Default Roles</a>
</li>
<li>
<a href="#">Credentials</a>
</li>
<li>
<a href="#">Token</a>
</li>
<li>
<a href="#">Keys</a>
</li>
<li>
<a href="#">Email</a>
</li>
</ul>
</li>
<li>
<a href="#">User</a>
</li>
<li>
<a href="#">Applications</a>
</li>
<li>
<a href="#">OAuth Clients</a>
</li>
<!--------------------------------------->
<li class="active pull-right" data-ng-show="auth.loggedIn">
<a class="button primary" href="#/create/realm" data-ng-class="path[0] == 'create' && path[1] == 'realm' && 'active'"
data-ng-show="auth.loggedIn">Add Realm</a>
</li>
</ul>
</div>
</nav>
</div>
</header>
<div class="container">
<div class="mock_view" data-ng-view id="view"></div>
<div id="loading" class="loading-backdrop">
<div class="loading">
<span>Loading...</span>
</div>
</div>
</div>
<script type="text/javascript">
$.idleTimeout('#idletimeout', '#idletimeout a', {
idleAfter: 300,
pollingInterval: 60,
keepAliveURL: '/auth/rest/admin/keepalive',
serverResponseEquals: '',
failedRequests: 1,
onTimeout: function(){
$(this).slideUp();
window.location = "/auth/rest/admin/logout";
},
onIdle: function(){
$(this).slideDown(); // show the warning bar
},
onCountdown: function( counter ){
$(this).find("span").html( counter ); // update the counter
},
onResume: function(){
$(this).slideUp(); // hide the warning bar
}
});
</script>
</body>
</html>
@@ -1,352 +0,0 @@
module.controller('ApplicationRoleListCtrl', function($scope, $location, realm, application, roles) {
$scope.realm = realm;
$scope.roles = roles;
$scope.application = application;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationCredentialsCtrl', function($scope, $location, realm, application, ApplicationCredentials, Notifications) {
$scope.realm = realm;
$scope.application = application;
var secret = ApplicationCredentials.get({ realm : realm.realm, application : application.name },
function() {
$scope.secret = secret.value;
}
);
$scope.changePassword = function() {
var secret = ApplicationCredentials.update({ realm : realm.realm, application : application.name },
function() {
Notifications.success('The secret has been changed.');
$scope.secret = secret.value;
},
function() {
Notifications.error("The secret was not changed due to a problem.");
$scope.secret = "error";
}
);
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationSessionsCtrl', function($scope, $location, realm, application) {
$scope.realm = realm;
$scope.application = application;
});
module.controller('ApplicationRoleDetailCtrl', function($scope, realm, application, role, roles, applications,
Role, ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.application = application;
$scope.role = angular.copy(role);
$scope.create = !role.name;
$scope.changed = $scope.create;
$scope.save = function() {
if ($scope.create) {
ApplicationRole.save({
realm: realm.realm,
application : application.name
}, $scope.role, function (data, headers) {
$scope.changed = false;
role = angular.copy($scope.role);
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + "/applications/" + application.name + "/roles/" + id);
Notifications.success("The role has been created.");
});
} else {
$scope.update();
}
};
$scope.remove = function() {
Dialog.confirmDelete($scope.role.name, 'role', function() {
$scope.role.$remove({
realm : realm.realm,
application : application.name,
role : $scope.role.name
}, function() {
$location.url("/realms/" + realm.realm + "/applications/" + application.name + "/roles");
Notifications.success("The role has been deleted.");
});
});
};
$scope.cancel = function () {
$location.url("/realms/" + realm.realm + "/applications/" + application.name + "/roles");
};
roleControl($scope, realm, role, roles, applications,
ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
$http, $location, Notifications, Dialog);
});
module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, $location) {
console.log('ApplicationListCtrl');
$scope.realm = realm;
$scope.applications = applications;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationInstallationCtrl', function($scope, realm, application, ApplicationInstallation,ApplicationInstallationJBoss, $http, $routeParams) {
console.log('ApplicationInstallationCtrl');
$scope.realm = realm;
$scope.application = application;
$scope.installation = null;
$scope.download = null;
$scope.configFormat = null;
$scope.configFormats = [
"keycloak.json",
"Wildfly/JBoss Subsystem XML"
];
$scope.changeFormat = function() {
if ($scope.configFormat == "keycloak.json") {
var url = ApplicationInstallation.url({ realm: $routeParams.realm, application: $routeParams.application });
var installation = $http.get(url).success(function(data) {
var tmp = angular.fromJson(data);
$scope.installation = angular.toJson(tmp, true);
})
$scope.download = url;
} else if ($scope.configFormat == "Wildfly/JBoss Subsystem XML") {
var url = ApplicationInstallationJBoss.url({ realm: $routeParams.realm, application: $routeParams.application });
var installation = $http.get(url).success(function(data) {
$scope.installation = data;
})
$scope.download = url;
}
};
});
module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {
console.log('ApplicationDetailCtrl');
$scope.realm = realm;
$scope.create = !application.name;
if (!$scope.create) {
$scope.application= angular.copy(application);
} else {
$scope.application = {};
$scope.application.webOrigins = [];
$scope.application.redirectUris = [];
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('application', function() {
if (!angular.equals($scope.application, application)) {
$scope.changed = true;
}
}, true);
$scope.deleteWebOrigin = function(index) {
$scope.application.webOrigins.splice(index, 1);
}
$scope.addWebOrigin = function() {
$scope.application.webOrigins.push($scope.newWebOrigin);
$scope.newWebOrigin = "";
}
$scope.deleteRedirectUri = function(index) {
$scope.application.redirectUris.splice(index, 1);
}
$scope.addRedirectUri = function() {
$scope.application.redirectUris.push($scope.newRedirectUri);
$scope.newRedirectUri = "";
}
$scope.save = function() {
if ($scope.create) {
Application.save({
realm: realm.realm,
application: ''
}, $scope.application, function (data, headers) {
$scope.changed = false;
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + "/applications/" + id);
Notifications.success("The application has been created.");
});
} else {
Application.update({
realm : realm.realm,
application : application.name
}, $scope.application, function() {
$scope.changed = false;
application = angular.copy($scope.application);
Notifications.success("Your changes have been saved to the application.");
});
}
};
$scope.reset = function() {
$scope.application = angular.copy(application);
$scope.changed = false;
};
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/applications");
};
$scope.remove = function() {
Dialog.confirmDelete($scope.application.name, 'application', function() {
$scope.application.$remove({
realm : realm.realm,
application : $scope.application.name
}, function() {
$location.url("/realms/" + realm.realm + "/applications");
Notifications.success("The application has been deleted.");
});
});
};
});
module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, application, roles, applications, ApplicationRealmScopeMapping, ApplicationApplicationScopeMapping, ApplicationRole) {
$scope.realm = realm;
$scope.application = application;
$scope.realmRoles = angular.copy(roles);
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
$scope.applications = applications;
$scope.applicationRoles = [];
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.applicationMappings = [];
$scope.realmMappings = ApplicationRealmScopeMapping.query({realm : realm.realm, application : application.name}, function(){
for (var i = 0; i < $scope.realmMappings.length; i++) {
var role = $scope.realmMappings[i];
for (var j = 0; j < $scope.realmRoles.length; j++) {
var realmRole = $scope.realmRoles[j];
if (realmRole.id == role.id) {
var idx = $scope.realmRoles.indexOf(realmRole);
if (idx != -1) {
$scope.realmRoles.splice(idx, 1);
break;
}
}
}
}
});
$scope.addRealmRole = function() {
$http.post('/auth/rest/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
$scope.selectedRealmRoles).success(function() {
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
var role = $scope.selectedRealmRoles[i];
var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
if (idx != -1) {
$scope.realmRoles.splice(idx, 1);
$scope.realmMappings.push(role);
}
}
$scope.selectRealmRoles = [];
});
};
$scope.deleteRealmRole = function() {
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
var role = $scope.selectedRealmMappings[i];
var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
if (idx != -1) {
$scope.realmMappings.splice(idx, 1);
$scope.realmRoles.push(role);
}
}
$scope.selectedRealmMappings = [];
});
};
$scope.addApplicationRole = function() {
$http.post('/auth/rest/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(function() {
for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
var role = $scope.selectedApplicationRoles[i];
var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
$scope.applicationMappings.push(role);
}
}
$scope.selectedApplicationRoles = [];
});
};
$scope.deleteApplicationRole = function() {
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
var role = $scope.selectedApplicationMappings[i];
var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
if (idx != -1) {
$scope.applicationMappings.splice(idx, 1);
$scope.applicationRoles.push(role);
}
}
$scope.selectedApplicationMappings = [];
});
};
$scope.changeApplication = function() {
if ($scope.targetApp) {
$scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.targetApp.name}, function() {
$scope.applicationMappings = ApplicationApplicationScopeMapping.query({realm : realm.realm, application : application.name, targetApp : $scope.targetApp.name}, function(){
for (var i = 0; i < $scope.applicationMappings.length; i++) {
var role = $scope.applicationMappings[i];
for (var j = 0; j < $scope.applicationRoles.length; j++) {
var realmRole = $scope.applicationRoles[j];
if (realmRole.id == role.id) {
var idx = $scope.applicationRoles.indexOf(realmRole);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
break;
}
}
}
}
});
}
);
} else {
$scope.applicationRoles = null;
}
};
});
@@ -1,255 +0,0 @@
module.controller('OAuthClientCredentialsCtrl', function($scope, $location, realm, oauth, OAuthClientCredentials, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
var secret = OAuthClientCredentials.get({ realm : realm.realm, oauth : oauth.id },
function() {
$scope.secret = secret.value;
}
);
$scope.changePassword = function() {
var secret = OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id },
function() {
Notifications.success('The secret has been changed.');
$scope.secret = secret.value;
},
function() {
Notifications.error("The secret was not changed due to a problem.");
$scope.secret = "error";
}
);
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, OAuthClient, $location) {
$scope.realm = realm;
$scope.oauthClients = oauthClients;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.create = !oauth.id;
if (!$scope.create) {
$scope.oauth= angular.copy(oauth);
} else {
$scope.oauth = {};
$scope.oauth.webOrigins = [];
$scope.oauth.redirectUris = [];
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('oauth', function() {
if (!angular.equals($scope.oauth, oauth)) {
$scope.changed = true;
}
}, true);
$scope.deleteWebOrigin = function(index) {
$scope.oauth.webOrigins.splice(index, 1);
}
$scope.addWebOrigin = function() {
$scope.oauth.webOrigins.push($scope.newWebOrigin);
$scope.newWebOrigin = "";
}
$scope.deleteRedirectUri = function(index) {
$scope.oauth.redirectUris.splice(index, 1);
}
$scope.addRedirectUri = function() {
$scope.oauth.redirectUris.push($scope.newRedirectUri);
$scope.newRedirectUri = "";
}
$scope.save = function() {
if ($scope.create) {
OAuthClient.save({
realm: realm.realm
}, $scope.oauth, function (data, headers) {
$scope.changed = false;
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + "/oauth-clients/" + id);
Notifications.success("The oauth client has been created.");
});
} else {
OAuthClient.update({
realm : realm.realm,
id : oauth.id
}, $scope.oauth, function() {
$scope.changed = false;
oauth = angular.copy($scope.oauth);
Notifications.success("Your changes have been saved to the oauth client.");
});
}
};
$scope.reset = function() {
$scope.oauth = angular.copy(oauth);
$scope.changed = false;
};
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/oauth-clients");
};
$scope.remove = function() {
Dialog.confirmDelete($scope.oauth.name, 'oauth', function() {
$scope.oauth.$remove({
realm : realm.realm,
id : $scope.oauth.id
}, function() {
$location.url("/realms/" + realm.realm + "/oauth-clients");
Notifications.success("The oauth client has been deleted.");
});
});
};
});
module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, roles, applications, OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole) {
$scope.realm = realm;
$scope.oauth = oauth;
$scope.realmRoles = angular.copy(roles);
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
$scope.applications = applications;
$scope.applicationRoles = [];
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.applicationMappings = [];
$scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id}, function(){
for (var i = 0; i < $scope.realmMappings.length; i++) {
var role = $scope.realmMappings[i];
for (var j = 0; j < $scope.realmRoles.length; j++) {
var realmRole = $scope.realmRoles[j];
if (realmRole.id == role.id) {
var idx = $scope.realmRoles.indexOf(realmRole);
if (idx != -1) {
$scope.realmRoles.splice(idx, 1);
break;
}
}
}
}
});
$scope.addRealmRole = function() {
$http.post('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
$scope.selectedRealmRoles).success(function() {
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
var role = $scope.selectedRealmRoles[i];
var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
if (idx != -1) {
$scope.realmRoles.splice(idx, 1);
$scope.realmMappings.push(role);
}
}
$scope.selectRealmRoles = [];
});
};
$scope.deleteRealmRole = function() {
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
var role = $scope.selectedRealmMappings[i];
var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
if (idx != -1) {
$scope.realmMappings.splice(idx, 1);
$scope.realmRoles.push(role);
}
}
$scope.selectedRealmMappings = [];
});
};
$scope.addApplicationRole = function() {
$http.post('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(function() {
for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
var role = $scope.selectedApplicationRoles[i];
var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
$scope.applicationMappings.push(role);
}
}
$scope.selectedApplicationRoles = [];
});
};
$scope.deleteApplicationRole = function() {
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
var role = $scope.selectedApplicationMappings[i];
var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
if (idx != -1) {
$scope.applicationMappings.splice(idx, 1);
$scope.applicationRoles.push(role);
}
}
$scope.selectedApplicationMappings = [];
});
};
$scope.changeApplication = function() {
if ($scope.targetApp) {
$scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.targetApp.name}, function() {
$scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name}, function(){
for (var i = 0; i < $scope.applicationMappings.length; i++) {
var role = $scope.applicationMappings[i];
for (var j = 0; j < $scope.applicationRoles.length; j++) {
var realmRole = $scope.applicationRoles[j];
if (realmRole.id == role.id) {
var idx = $scope.applicationRoles.indexOf(realmRole);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
break;
}
}
}
}
});
}
);
} else {
$scope.targetApp = null;
}
};
});
module.controller('OAuthClientInstallationCtrl', function($scope, realm, installation, oauth, OAuthClientInstallation, $routeParams) {
$scope.realm = realm;
$scope.oauth = oauth;
$scope.installation = installation;
$scope.download = OAuthClientInstallation.url({ realm: $routeParams.realm, oauth: $routeParams.oauth });
});
@@ -1,457 +0,0 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
/**
* @ngdoc overview
* @name ngResource
* @description
*/
/**
* @ngdoc object
* @name ngResource.$resource
* @requires $http
*
* @description
* A factory which creates a resource object that lets you interact with
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
*
* The returned resource object has action methods which provide high-level behaviors without
* the need to interact with the low level {@link ng.$http $http} service.
*
* # Installation
* To use $resource make sure you have included the `angular-resource.js` that comes in Angular
* package. You can also find this file on Google CDN, bower as well as at
* {@link http://code.angularjs.org/ code.angularjs.org}.
*
* Finally load the module in your application:
*
* angular.module('app', ['ngResource']);
*
* and you are ready to get started!
*
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), you'll need to escape the colon character before the port
* number, like this: `$resource('http://example.com\\:8080/api')`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods.
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
*
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from
* the data object (useful for non-GET operations).
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
* default set of resource actions. The declaration should be created in the following format:
*
* {action1: {method:?, params:?, isArray:?},
* action2: {method:?, params:?, isArray:?},
* ...}
*
* Where:
*
* - `action` – {string} – The name of action. This name becomes the name of the method on your
* resource object.
* - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
* and `JSONP`
* - `params` – {object=} – Optional set of pre-bound parameters for this action.
* - isArray – {boolean=} – If true then the returned object for this action is an array, see
* `returns` section.
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
*
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* <pre>
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
</pre>
*
* It is important to realize that invoking a $resource object method immediately returns an
* empty reference (object or array depending on `isArray`). Once the data is returned from the
* server the existing reference is populated with the actual data. This is a useful trick since
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
* object results in no rendering, once the data arrives from the server then the object is
* populated with the data and the view automatically re-renders itself showing the new data. This
* means that in most case one never has to write a callback function for the action methods.
*
* The action methods on the class object or instance object can be invoked with the following
* parameters:
*
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
*
* @example
*
* # Credit card resource
*
* <pre>
// Define CreditCard class
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'@id'}, {
charge: {method:'POST', params:{charge:true}}
});
// We can retrieve a collection from the server
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
// we can create an instance as well
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* </pre>
*
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
*
* Calling these methods invoke `$http` on the `url` template with the given `method` and `params`.
* When the data is returned from the server then the object is an instance of the resource type and
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
<pre>
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
</pre>
*
* It's worth noting that the success callback for `get`, `query` and other method gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
<pre>
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
u.$save(function(u, putResponseHeaders) {
//u => saved user object
//putResponseHeaders => $http header getter
});
});
</pre>
* # Buzz client
Let's look at what a buzz client created with the `$resource` service looks like:
<doc:example>
<doc:source jsfiddle="false">
<script>
function BuzzController($resource) {
this.userId = 'googlebuzz';
this.Activity = $resource(
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
{alt:'json', callback:'JSON_CALLBACK'},
{get:{method:'JSONP', params:{visibility:'@self'}}, replies: {method:'JSONP', params:{visibility:'@self', comments:'@comments'}}}
);
}
BuzzController.prototype = {
fetch: function() {
this.activities = this.Activity.get({userId:this.userId});
},
expandReplies: function(activity) {
activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id});
}
};
BuzzController.$inject = ['$resource'];
</script>
<div ng-controller="BuzzController">
<input ng-model="userId"/>
<button ng-click="fetch()">fetch</button>
<hr/>
<div ng-repeat="item in activities.data.items">
<h1 style="font-size: 15px;">
<img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
<a href ng-click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a>
</h1>
{{item.object.content | html}}
<div ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
<img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}}
</div>
</div>
</div>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$parse', function($http, $parse) {
var DEFAULT_ACTIONS = {
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
var noop = angular.noop,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
isFunction = angular.isFunction,
getter = function(obj, path) {
return $parse(path)(obj);
};
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
* segments:
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriSegment(val) {
return encodeUriQuery(val, true).
replace(/%26/gi, '&').
replace(/%3D/gi, '=').
replace(/%2B/gi, '+');
}
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
* method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
* encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
function Route(template, defaults) {
this.template = template = template + '#';
this.defaults = defaults || {};
var urlParams = this.urlParams = {};
forEach(template.split(/\W/), function(param){
if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) {
urlParams[param] = true;
}
});
this.template = template.replace(/\\:/g, ':');
}
Route.prototype = {
url: function(params) {
var self = this,
url = this.template,
val,
encodedVal;
params = params || {};
forEach(this.urlParams, function(_, urlParam){
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W)", "g"), encodedVal + "$1");
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
}
});
url = url.replace(/\/?#$/, '');
var query = [];
forEach(params, function(value, key){
if (!self.urlParams[key]) {
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
}
});
query.sort();
url = url.replace(/\/*$/, '');
return url + (query.length ? '?' + query.join('&') : '');
}
};
function ResourceFactory(url, paramDefaults, actions) {
var route = new Route(url);
actions = extend({}, DEFAULT_ACTIONS, actions);
function extractParams(data, actionParams){
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key){
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
});
return ids;
}
function Resource(value){
copy(value || {}, this);
}
forEach(actions, function(action, name) {
action.method = angular.uppercase(action.method);
var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
Resource[name] = function(a1, a2, a3, a4) {
var params = {};
var data;
var success = noop;
var error = null;
switch(arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
}
case 1:
if (isFunction(a1)) success = a1;
else if (hasBody) data = a1;
else params = a1;
break;
case 0: break;
default:
throw "Expected between 0-4 arguments [params, data, success, error], got " +
arguments.length + " arguments.";
}
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
$http({
method: action.method,
url: route.url(extend({}, extractParams(data, action.params || {}), params)),
data: data
}).then(function(response) {
var data = response.data;
if (data) {
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
copy(data, value);
}
}
(success||noop)(value, response.headers);
}, error);
return value;
};
Resource.prototype['$' + name] = function(a1, a2, a3) {
var params = extractParams(this),
success = noop,
error;
switch(arguments.length) {
case 3: params = a1; success = a2; error = a3; break;
case 2:
case 1:
if (isFunction(a1)) {
success = a1;
error = a2;
} else {
params = a1;
success = a2 || noop;
}
case 0: break;
default:
throw "Expected between 1-3 arguments [params, success, error], got " +
arguments.length + " arguments.";
}
var data = hasBody ? this : undefined;
Resource[name].call(this, params, data, success, error);
};
});
Resource.bind = function(additionalParamDefaults){
return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return ResourceFactory;
}]);
})(window, window.angular);
@@ -1,537 +0,0 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
/**
* @ngdoc overview
* @name ngSanitize
* @description
*/
/*
* HTML Parser By Misko Hevery (misko@hevery.com)
* based on: HTML Parser By John Resig (ejohn.org)
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* // Use like so:
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
*/
/**
* @ngdoc service
* @name ngSanitize.$sanitize
* @function
*
* @description
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
* then serialized back to properly escaped html string. This means that no unsafe input can make
* it into the returned string, however, since our parser is more strict than a typical browser
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
* browser, won't make it through the sanitizer.
*
* @param {string} html Html input.
* @returns {string} Sanitized html.
*
* @example
<doc:example module="ngSanitize">
<doc:source>
<script>
function Ctrl($scope) {
$scope.snippet =
'<p style="color:blue">an html\n' +
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
'snippet</p>';
}
</script>
<div ng-controller="Ctrl">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Filter</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="html-filter">
<td>html filter</td>
<td>
<pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
<tr id="html-unsafe-filter">
<td>unsafe html filter</td>
<td><pre>&lt;div ng-bind-html-unsafe="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html-unsafe="snippet"></div></td>
</tr>
</table>
</div>
</doc:source>
<doc:scenario>
it('should sanitize the html snippet ', function() {
expect(using('#html-filter').element('div').html()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
});
it('should escape snippet without any filter', function() {
expect(using('#escaped-html').element('div').html()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
it('should inline raw snippet if filtered as unsafe', function() {
expect(using('#html-unsafe-filter').element("div").html()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should update', function() {
input('snippet').enter('new <b>text</b>');
expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
expect(using('#escaped-html').element('div').html()).toBe("new &lt;b&gt;text&lt;/b&gt;");
expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
});
</doc:scenario>
</doc:example>
*/
var $sanitize = function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf));
return buf.join('');
};
// Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
BEGIN_TAG_REGEXP = /^</,
BEGING_END_TAGE_REGEXP = /^<\s*\//,
COMMENT_REGEXP = /<!--(.*?)-->/g,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = makeMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
optionalEndTagInlineElements = makeMap("rp,rt"),
optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements);
// Safe Block Elements - HTML5
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," +
"blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," +
"header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
// Inline Elements - HTML5
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," +
"big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," +
"span,strike,strong,sub,sup,time,tt,u,var"));
// Special Elements (can contain anything)
var specialElements = makeMap("script,style");
var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
//Attributes that have href and hence need to be sanitized
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
var validAttrs = angular.extend({}, uriAttrs, makeMap(
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
'scope,scrolling,shape,span,start,summary,target,title,type,'+
'valign,value,vspace,width'));
function makeMap(str) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) obj[items[i]] = true;
return obj;
}
/**
* @example
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* @param {string} html string
* @param {object} handler
*/
function htmlParser( html, handler ) {
var index, chars, match, stack = [], last = html;
stack.last = function() { return stack[ stack.length - 1 ]; };
while ( html ) {
chars = true;
// Make sure we're not in a script or style element
if ( !stack.last() || !specialElements[ stack.last() ] ) {
// Comment
if ( html.indexOf("<!--") === 0 ) {
index = html.indexOf("-->");
if ( index >= 0 ) {
if (handler.comment) handler.comment( html.substring( 4, index ) );
html = html.substring( index + 3 );
chars = false;
}
// end tag
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
match = html.match( END_TAG_REGEXP );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( END_TAG_REGEXP, parseEndTag );
chars = false;
}
// start tag
} else if ( BEGIN_TAG_REGEXP.test(html) ) {
match = html.match( START_TAG_REGEXP );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( START_TAG_REGEXP, parseStartTag );
chars = false;
}
}
if ( chars ) {
index = html.indexOf("<");
var text = index < 0 ? html : html.substring( 0, index );
html = index < 0 ? "" : html.substring( index );
if (handler.chars) handler.chars( decodeEntities(text) );
}
} else {
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){
text = text.
replace(COMMENT_REGEXP, "$1").
replace(CDATA_REGEXP, "$1");
if (handler.chars) handler.chars( decodeEntities(text) );
return "";
});
parseEndTag( "", stack.last() );
}
if ( html == last ) {
throw "Parse Error: " + html;
}
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag( tag, tagName, rest, unary ) {
tagName = angular.lowercase(tagName);
if ( blockElements[ tagName ] ) {
while ( stack.last() && inlineElements[ stack.last() ] ) {
parseEndTag( "", stack.last() );
}
}
if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) {
parseEndTag( "", tagName );
}
unary = voidElements[ tagName ] || !!unary;
if ( !unary )
stack.push( tagName );
var attrs = {};
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
var value = doubleQuotedValue
|| singleQoutedValue
|| unqoutedValue
|| '';
attrs[name] = decodeEntities(value);
});
if (handler.start) handler.start( tagName, attrs, unary );
}
function parseEndTag( tag, tagName ) {
var pos = 0, i;
tagName = angular.lowercase(tagName);
if ( tagName )
// Find the closest opened tag of the same type
for ( pos = stack.length - 1; pos >= 0; pos-- )
if ( stack[ pos ] == tagName )
break;
if ( pos >= 0 ) {
// Close all the open elements, up the stack
for ( i = stack.length - 1; i >= pos; i-- )
if (handler.end) handler.end( stack[ i ] );
// Remove the open elements from the stack
stack.length = pos;
}
}
}
/**
* decodes all entities into regular string
* @param value
* @returns {string} A string with decoded entities.
*/
var hiddenPre=document.createElement("pre");
function decodeEntities(value) {
hiddenPre.innerHTML=value.replace(/</g,"&lt;");
return hiddenPre.innerText || hiddenPre.textContent || '';
}
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(NON_ALPHANUMERIC_REGEXP, function(value){
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* create an HTML/XML writer which writes to buffer
* @param {Array} buf use buf.jain('') to get out sanitized html string
* @returns {object} in the form of {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf){
var ignore = false;
var out = angular.bind(buf, buf.push);
return {
start: function(tag, attrs, unary){
tag = angular.lowercase(tag);
if (!ignore && specialElements[tag]) {
ignore = tag;
}
if (!ignore && validElements[tag] == true) {
out('<');
out(tag);
angular.forEach(attrs, function(value, key){
var lkey=angular.lowercase(key);
if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
out(' ');
out(key);
out('="');
out(encodeEntities(value));
out('"');
}
});
out(unary ? '/>' : '>');
}
},
end: function(tag){
tag = angular.lowercase(tag);
if (!ignore && validElements[tag] == true) {
out('</');
out(tag);
out('>');
}
if (tag == ignore) {
ignore = false;
}
},
chars: function(chars){
if (!ignore) {
out(encodeEntities(chars));
}
}
};
}
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
/**
* @ngdoc directive
* @name ngSanitize.directive:ngBindHtml
*
* @description
* Creates a binding that will sanitize the result of evaluating the `expression` with the
* {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
*
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
*
* @element ANY
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
*/
angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
return function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
value = $sanitize(value);
element.html(value || '');
});
};
}]);
/**
* @ngdoc filter
* @name ngSanitize.filter:linky
* @function
*
* @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* @param {string} text Input text.
* @returns {string} Html-linkified text.
*
* @usage
<span ng-bind-html="linky_expression | linky"></span>
*
* @example
<doc:example module="ngSanitize">
<doc:source>
<script>
function Ctrl($scope) {
$scope.snippet =
'Pretty text with some links:\n'+
'http://angularjs.org/,\n'+
'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.';
}
</script>
<div ng-controller="Ctrl">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Filter</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="linky-filter">
<td>linky filter</td>
<td>
<pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</doc:source>
<doc:scenario>
it('should linkify the snippet with urls', function() {
expect(using('#linky-filter').binding('snippet | linky')).
toBe('Pretty text with some links:&#10;' +
'<a href="http://angularjs.org/">http://angularjs.org/</a>,&#10;' +
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>,&#10;' +
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>,&#10;' +
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
});
it ('should not linkify snippet without the linky filter', function() {
expect(using('#escaped-html').binding('snippet')).
toBe("Pretty text with some links:\n" +
"http://angularjs.org/,\n" +
"mailto:us@somewhere.org,\n" +
"another@somewhere.org,\n" +
"and one more: ftp://127.0.0.1/.");
});
it('should update', function() {
input('snippet').enter('new http://link.');
expect(using('#linky-filter').binding('snippet | linky')).
toBe('new <a href="http://link">http://link</a>.');
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
});
</doc:scenario>
</doc:example>
*/
angular.module('ngSanitize').filter('linky', function() {
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
MAILTO_REGEXP = /^mailto:/;
return function(text) {
if (!text) return text;
var match;
var raw = text;
var html = [];
// TODO(vojta): use $sanitize instead
var writer = htmlSanitizeWriter(html);
var url;
var i;
while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence
url = match[0];
// if we did not match ftp/http/mailto then assume mailto
if (match[2] == match[3]) url = 'mailto:' + url;
i = match.index;
writer.chars(raw.substr(0, i));
writer.start('a', {href:url});
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
writer.end('a');
raw = raw.substring(i + match[0].length);
}
writer.chars(raw);
return html.join('');
};
});
})(window, window.angular);
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,195 +0,0 @@
/**
* Enhanced Select2 Dropmenus
*
* @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
*/
angular.module('ui.select2', []).value('uiSelect2Config', {}).directive('uiSelect2', ['uiSelect2Config', '$timeout', function (uiSelect2Config, $timeout) {
var options = {};
if (uiSelect2Config) {
angular.extend(options, uiSelect2Config);
}
return {
require: 'ngModel',
compile: function (tElm, tAttrs) {
var watch,
repeatOption,
repeatAttr,
isSelect = tElm.is('select'),
isMultiple = (tAttrs.multiple !== undefined);
// Enable watching of the options dataset if in use
if (tElm.is('select')) {
repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]');
if (repeatOption.length) {
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
}
}
return function (scope, elm, attrs, controller) {
// instance-specific options
var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2));
/*
Convert from Select2 view-model to Angular view-model.
*/
var convertToAngularModel = function(select2_data) {
var model;
if (opts.simple_tags) {
model = []
angular.forEach(select2_data, function(value, index) {
model.push(value.id)
})
} else {
model = select2_data
}
return model
}
/*
Convert from Angular view-model to Select2 view-model.
*/
var convertToSelect2Model = function(angular_data) {
var model = []
if (!angular_data) {
return model;
}
if (opts.simple_tags) {
model = [];
angular.forEach(
angular_data,
function(value, index) {
model.push({'id': value, 'text': value});
})
} else {
model = angular_data;
}
return model
}
if (isSelect) {
// Use <select multiple> instead
delete opts.multiple;
delete opts.initSelection;
} else if (isMultiple) {
opts.multiple = true;
}
if (controller) {
// Watch the model for programmatic changes
scope.$watch(tAttrs.ngModel, function(current, old) {
if (!current) {
return
}
if (current == old) {
return
}
controller.$render()
}, true)
controller.$render = function () {
if (isSelect) {
elm.select2('val', controller.$viewValue);
} else {
if (opts.multiple) {
elm.select2(
'data', convertToSelect2Model(controller.$viewValue));
} else {
if (angular.isObject(controller.$viewValue)) {
elm.select2('data', controller.$viewValue);
} else if (!controller.$viewValue) {
elm.select2('data', null);
} else {
elm.select2('val', controller.$viewValue);
}
}
}
};
// Watch the options dataset for changes
if (watch) {
scope.$watch(watch, function (newVal, oldVal, scope) {
if (!newVal) return;
// Delayed so that the options have time to be rendered
$timeout(function () {
elm.select2('val', controller.$viewValue);
// Refresh angular to remove the superfluous option
elm.trigger('change');
});
});
}
// Update valid and dirty statuses
controller.$parsers.push(function (value) {
var div = elm.prev()
div
.toggleClass('ng-invalid', !controller.$valid)
.toggleClass('ng-valid', controller.$valid)
.toggleClass('ng-invalid-required', !controller.$valid)
.toggleClass('ng-valid-required', controller.$valid)
.toggleClass('ng-dirty', controller.$dirty)
.toggleClass('ng-pristine', controller.$pristine);
return value;
});
if (!isSelect) {
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
elm.bind("change", function () {
if (scope.$$phase) return;
scope.$apply(function () {
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
});
if (opts.initSelection) {
var initSelection = opts.initSelection;
opts.initSelection = function (element, callback) {
initSelection(element, function (value) {
controller.$setViewValue(convertToAngularModel(value));
callback(value);
});
};
}
}
}
elm.bind("$destroy", function() {
elm.select2("destroy");
});
attrs.$observe('disabled', function (value) {
elm.select2('enable', !value);
});
attrs.$observe('readonly', function (value) {
elm.select2('readonly', !!value);
});
if (attrs.ngMultiple) {
scope.$watch(attrs.ngMultiple, function(newVal) {
elm.select2(opts);
});
}
// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
$timeout(function () {
elm.select2(opts);
// Set initial value - I'm not sure about this but it seems to need to be there
elm.val(controller.$viewValue);
// important!
controller.$render();
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect)
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
};
}
};
}]);
@@ -1,48 +0,0 @@
<nav id="kc-navigation" class="navbar" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<h1 class="navbar-title"></h1>
</div>
<div class="collapse navbar-collapse navbar-collapse-1">
<ul class="nav navbar-nav navbar-utility" data-ng-show="auth.loggedIn">
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="pficon pficon-user"></span>
{{auth.user.displayName}}<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/auth/rest/realms/keycloak-admin/account?referrer=admin-console">Manage Account</a></li>
<li class="separator"><a href="/auth/rest/admin/logout">Sign Out</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-primary persistent-secondary" data-ng-controller="RealmDropdownCtrl">
<li class="dropdown context" data-ng-show="showNav()">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
{{current.realm.realm}}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li data-ng-repeat="realm in current.realms">
<a href="" ng-click="changeRealm(realm.realm)">{{realm.realm}}</a>
</li>
</ul>
</li>
<li class="active pull-right" data-ng-show="auth.loggedIn">
<a class="button primary" href="#/create/realm" data-ng-class="path[0] == 'create' && path[1] == 'realm' && 'active'"
data-ng-show="auth.loggedIn">Add Realm</a>
</li>
</ul>
</div>
</nav>
<!-- TODO remove once this page is properly styled -->
<style type="text/css">
.icon-spinner6 {
cursor: pointer;
}
</style>
@@ -1,104 +0,0 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li class="active">Add OAuth Client</li>
</ol>
<h2 data-ng-show="create"><span>{{realm.realm}}</span> Add OAuth Client</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Settings</h2>
<form class="form-horizontal" name="oauthForm" novalidate>
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
<div class="col-sm-4">
<input class="form-control" type="text" id="name" name="name" data-ng-model="oauth.name" autofocus
required>
</div>
</div>
<div class="form-group clearfix block">
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
<div class="col-sm-4">
<input ng-model="oauth.enabled" name="enabled" id="enabled" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
<div class="col-sm-4">
<div ng-repeat="webOrigin in oauth.webOrigins" class="kc-item-deletable row">
<div class="col-sm-9">
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
name="webOrigin" id="webOrigin" data-ng-model="webOrigin" readonly />
</div>
<div class="col-sm-2">
<button class="btn btn-danger" type="button" data-ng-click="deleteWebOrigin($index)">
Delete</button>
</div>
</div>
<div class="row">
<div class="col-sm-9">
<input class="form-control" type="text" name="newWebOrigin" id="newWebOrigin"
placeholder="New Web Origin..." data-ng-model="newWebOrigin"
data-ng-class="{'input-below':oauth.webOrigins.length}" />
</div>
<div class="col-sm-2">
<button class="btn btn-primary" data-ng-click="addWebOrigin()" ng-show="newWebOrigin.length > 0">Add</button>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI</label>
<div class="col-sm-4">
<div ng-repeat="redirectUri in oauth.redirectUris" class="kc-item-deletable row">
<div class="col-sm-9">
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
name="redirectUri" id="redirectUri" data-ng-model="redirectUri" readonly />
</div>
<div class="col-sm-2">
<button class="btn btn-danger" type="button" data-ng-click="deleteRedirectUri($index)">
Delete</button>
</div>
</div>
<div class="row">
<div class="col-sm-9">
<input class="form-control" type="text" name="newRedirectUri" id="newRedirectUri"
placeholder="New Redirect URI..." data-ng-model="newRedirectUri"
data-ng-class="{'input-below':oauth.redirectUris.length}" />
</div>
<div class="col-sm-2">
<button class="btn btn-primary" data-ng-click="addRedirectUri()" ng-show="newRedirectUri.length > 0">Add</button>
</div>
</div>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
</div>
</form>
</div>
</div>
@@ -1,104 +0,0 @@
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
<div id="content-area" class="col-sm-9" role="main">
<kc-navigation data-kc-current="general" data-kc-realm="realm.realm" data-kc-social="social"></kc-navigation>
<div id="content">
<ol class="breadcrumb" data-ng-hide="createRealm">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li class="active">General</li>
</ol>
<h2 class="pull-left" data-ng-show="createRealm">Add Realm</h2>
<h2 data-ng-hide="createRealm"><span>{{realm.realm}}</span> General Settings</h2>
<p class="subtitle" data-ng-show="createRealm"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<legend class="aj-collapse open"><span class="text">Required Settings</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Name <span class="required" data-ng-show="createRealm">*</span></label>
<div class="col-sm-4">
<input class="form-control" type="text" id="name" name="name" data-ng-model="realm.realm" autofocus required>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
<div class="col-sm-4">
<input ng-model="realm.enabled" name="enabled" id="enabled" onoffswitch />
</div>
</div>
</fieldset>
<fieldset>
<legend><span class="text">Login Options</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="social">Social login</label>
<div class="col-sm-4">
<input ng-model="realm.social" name="social" id="social" onoffswitch />
</div>
</div>
<div class="form-group" data-ng-show="realm.social">
<label class="col-sm-2 control-label" for="updateProfileOnInitialSocialLogin">Update profile on first social login</label>
<div class="col-sm-4">
<input ng-model="realm.updateProfileOnInitialSocialLogin" name="updateProfileOnInitialSocialLogin" id="updateProfileOnInitialSocialLogin" onoffswitch />
</div>
</div>
<div class="form-group">
<label for="registrationAllowed" class="col-sm-2 control-label">User registration</label>
<div class="col-sm-4">
<input ng-model="realm.registrationAllowed" name="registrationAllowed" id="registrationAllowed" onoffswitch />
</div>
</div>
<div class="form-group">
<label for="resetPasswordAllowed" class="col-sm-2 control-label">Reset password</label>
<div class="col-sm-4">
<input ng-model="realm.resetPasswordAllowed" name="resetPasswordAllowed" id="resetPasswordAllowed" onoffswitch />
</div>
</div>
<div class="form-group">
<label for="verifyEmail" class="col-sm-2 control-label">Verify email</label>
<div class="col-sm-4">
<input ng-model="realm.verifyEmail" name="verifyEmail" id="verifyEmail" onoffswitch />
</div>
</div>
<div class="form-group">
<label for="requireSsl" class="col-sm-2 control-label">Require SSL</label>
<div class="col-sm-4">
<input ng-model="realm.requireSsl" name="requireSsl" id="requireSsl" onoffswitch />
</div>
</div>
</fieldset>
<fieldset>
<legend><span class="text">Optional Settings</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="loginTheme">Login Theme</label>
<div class="col-sm-4">
<select kc-select id="loginTheme"
data-kc-placeholder="Select one..."
data-kc-model="realm.loginTheme"
data-kc-options="serverInfo.themes.login">
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="accountTheme">Account Theme</label>
<div class="col-sm-4">
<select kc-select id="accountTheme"
data-kc-placeholder="Select one..."
data-kc-model="realm.accountTheme"
data-kc-options="serverInfo.themes.account">
</select>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="createRealm">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!createRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
</div>
</form>
</div>
</div>
@@ -1,34 +0,0 @@
<div id="wrapper" class="container">
<div class="row">
<div class="bs-sidebar col-md-3 clearfix">
<ul data-ng-hide="createRealm">
<li class="active"><a href="#/realms">Realms</a></li>
</ul>
</div>
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav" data-ng-hide="createRealm">
<ul class="rcue-tabs">
</ul>
</div>
<div id="content">
<h1>
<span class="gray">Realms</span>
</h1>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Realm</th>
</tr>
</thead>
<tr data-ng-repeat="r in realms">
<td><a href="#/realms/{{r.realm}}">{{r.realm}}</a></td>
</tr>
</table>
</div>
</div>
<div id="container-right-bg"></div>
</div>
</div>
@@ -1,10 +0,0 @@
<ul data-ng-hide="createRealm">
<li data-ng-class="((!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' ||
path[2] == 'social-settings' || path[2] == 'required-credentials' || path[2] == 'default-roles' || path[2] == 'registration-settings' ||
path[2] == 'keys-settings' || path[2] == 'smtp-settings') && path[3] != 'applications') && 'active'"><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users">Users</a>
</li>
<li data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
</ul>
@@ -1,14 +0,0 @@
<div class="modal-dialog" tabindex="-1" role="dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
<span class="pficon pficon-close"></span>
</button>
<h4 class="modal-title" id="myModalLabel">{{ title }}</h4>
</div>
<div class="modal-body"><p ng-bind-html-unsafe="message"></p></div>
<div class="modal-footer">
<button ng-repeat="btn in buttons" ng-click="close(btn.result)" class="" ng-class="btn.cssClass">{{ btn.label }}</button>
</div>
</div>
</div>
@@ -1,10 +0,0 @@
<ul class="nav nav-tabs nav-tabs-pf">
<li ng-class="{active: kcCurrent == 'general'}"><a href="#/realms/{{kcRealm}}">General</a></li>
<li ng-class="{active: kcCurrent == 'social'}" data-ng-show="kcSocial"><a href="#/realms/{{kcRealm}}/social-settings">Social</a></li>
<li ng-class="{active: kcCurrent == 'roles'}"><a href="#/realms/{{kcRealm}}/roles">Roles</a></li>
<li ng-class="{active: kcCurrent == 'defRoles'}"><a href="#/realms/{{kcRealm}}/default-roles">Default Roles</a></li>
<li ng-class="{active: kcCurrent == 'credentials'}"><a href="#/realms/{{kcRealm}}/required-credentials">Credentials</a></li>
<li ng-class="{active: kcCurrent == 'token'}"><a href="#/realms/{{kcRealm}}/token-settings">Token</a></li>
<li ng-class="{active: kcCurrent == 'keys'}"><a href="#/realms/{{kcRealm}}/keys-settings">Keys</a></li>
<li ng-class="{active: kcCurrent == 'email'}"><a href="#/realms/{{kcRealm}}/smtp-settings">Email</a></li>
</ul>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd ">
</web-fragment>
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-api</artifactId>
<name>Keycloak Audit API</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,149 @@
package org.keycloak.audit;
import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import java.util.HashMap;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Audit {
private static final Logger log = Logger.getLogger(Audit.class);
private List<AuditListener> listeners;
private Event event;
public Audit(List<AuditListener> listeners, RealmModel realm, String ipAddress) {
this.listeners = listeners;
this.event = new Event();
realm(realm);
ipAddress(ipAddress);
}
Audit() {
}
public Audit realm(RealmModel realm) {
event.setRealmId(realm.getId());
return this;
}
public Audit realm(String realmId) {
event.setRealmId(realmId);
return this;
}
public Audit client(ClientModel client) {
event.setClientId(client.getClientId());
return this;
}
public Audit client(String clientId) {
event.setClientId(clientId);
return this;
}
public Audit user(UserModel user) {
event.setUserId(user.getId());
return this;
}
public Audit user(String userId) {
event.setUserId(userId);
return this;
}
public Audit session(UserSessionModel session) {
event.setSessionId(session.getId());
return this;
}
public Audit session(String sessionId) {
event.setSessionId(sessionId);
return this;
}
public Audit ipAddress(String ipAddress) {
event.setIpAddress(ipAddress);
return this;
}
public Audit event(EventType e) {
event.setEvent(e);
return this;
}
public Audit detail(String key, String value) {
if (value == null || value.equals("")) {
return this;
}
if (event.getDetails() == null) {
event.setDetails(new HashMap<String, String>());
}
event.getDetails().put(key, value);
return this;
}
public Audit removeDetail(String key) {
if (event.getDetails() != null) {
event.getDetails().remove(key);
}
return this;
}
public Event getEvent() {
return event;
}
public void success() {
send();
}
public void error(String error) {
event.setEvent(EventType.valueOf(event.getEvent().name() + "_ERROR"));
event.setError(error);
send();
}
public Audit clone() {
Audit clone = new Audit();
clone.listeners = listeners;
clone.event = event.clone();
return clone;
}
public Audit reset() {
Event old = event;
event = new Event();
event.setRealmId(old.getRealmId());
event.setIpAddress(old.getIpAddress());
event.setClientId(old.getClientId());
event.setUserId(old.getUserId());
return this;
}
private void send() {
event.setTime(System.currentTimeMillis());
if (listeners != null) {
for (AuditListener l : listeners) {
try {
l.onEvent(event);
} catch (Throwable t) {
log.error("Failed to send event to " + l, t);
}
}
}
}
}
@@ -0,0 +1,12 @@
package org.keycloak.audit;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface AuditListener extends Provider {
public void onEvent(Event event);
}
@@ -0,0 +1,10 @@
package org.keycloak.audit;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface AuditListenerFactory extends ProviderFactory<AuditListener> {
}
@@ -0,0 +1,27 @@
package org.keycloak.audit;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuditListenerSpi implements Spi {
@Override
public String getName() {
return "audit-listener";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuditListener.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuditListenerFactory.class;
}
}
@@ -0,0 +1,16 @@
package org.keycloak.audit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface AuditProvider extends AuditListener {
public EventQuery createQuery();
public void clear();
public void clear(String realmId);
public void clear(String realmId, long olderThan);
}
@@ -0,0 +1,10 @@
package org.keycloak.audit;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface AuditProviderFactory extends ProviderFactory<AuditProvider> {
}
@@ -0,0 +1,27 @@
package org.keycloak.audit;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuditProviderSpi implements Spi {
@Override
public String getName() {
return "audit";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuditProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuditProviderFactory.class;
}
}
@@ -0,0 +1,22 @@
package org.keycloak.audit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Details {
String EMAIL = "email";
String PREVIOUS_EMAIL = "previous_email";
String UPDATED_EMAIL = "updated_email";
String CODE_ID = "code_id";
String REDIRECT_URI = "redirect_uri";
String RESPONSE_TYPE = "response_type";
String AUTH_METHOD = "auth_method";
String REGISTER_METHOD = "register_method";
String USERNAME = "username";
String REMEMBER_ME = "remember_me";
String TOKEN_ID = "token_id";
String REFRESH_TOKEN_ID = "refresh_token_id";
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
}
+41
View File
@@ -0,0 +1,41 @@
package org.keycloak.audit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Errors {
String REALM_DISABLED = "realm_disabled";
String CLIENT_NOT_FOUND = "client_not_found";
String CLIENT_DISABLED = "client_disabled";
String INVALID_CLIENT_CREDENTIALS = "invalid_client_credentials";
String USER_NOT_FOUND = "user_not_found";
String USER_DISABLED = "user_disabled";
String USER_TEMPORARILY_DISABLED = "user_temporarily_disabled";
String INVALID_USER_CREDENTIALS = "invalid_user_credentials";
String USERNAME_MISSING = "username_missing";
String USERNAME_IN_USE = "username_in_use";
String INVALID_REDIRECT_URI = "invalid_redirect_uri";
String INVALID_CODE = "invalid_code";
String INVALID_TOKEN = "invalid_token";
String INVALID_REGISTRATION = "invalid_registration";
String INVALID_FORM = "invalid_form";
String REGISTRATION_DISABLED = "registration_disabled";
String REJECTED_BY_USER = "rejected_by_user";
String NOT_ALLOWED = "not_allowed";
String SOCIAL_PROVIDER_NOT_FOUND = "social_provider_not_found";
String SOCIAL_ID_IN_USE = "social_id_in_use";
String USER_NOT_LOGGED_IN = "user_not_logged_in";
String USER_SESSION_NOT_FOUND = "user_session_not_found";
}
@@ -0,0 +1,115 @@
package org.keycloak.audit;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Event {
private long time;
private EventType event;
private String realmId;
private String clientId;
private String userId;
private String sessionId;
private String ipAddress;
private String error;
private Map<String, String> details;
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public EventType getEvent() {
return event;
}
public void setEvent(EventType event) {
this.event = event;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public Map<String, String> getDetails() {
return details;
}
public void setDetails(Map<String, String> details) {
this.details = details;
}
public Event clone() {
Event clone = new Event();
clone.time = time;
clone.event = event;
clone.realmId = realmId;
clone.clientId = clientId;
clone.userId = userId;
clone.sessionId = sessionId;
clone.ipAddress = ipAddress;
clone.error = error;
clone.details = details != null ? new HashMap<String, String>(details) : null;
return clone;
}
}
@@ -0,0 +1,26 @@
package org.keycloak.audit;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface EventQuery {
public EventQuery event(EventType... events);
public EventQuery realm(String realmId);
public EventQuery client(String clientId);
public EventQuery user(String userId);
public EventQuery ipAddress(String ipAddress);
public EventQuery firstResult(int result);
public EventQuery maxResults(int results);
public List<Event> getResultList();
}
@@ -0,0 +1,42 @@
package org.keycloak.audit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public enum EventType {
LOGIN,
LOGIN_ERROR,
REGISTER,
REGISTER_ERROR,
LOGOUT,
LOGOUT_ERROR,
CODE_TO_TOKEN,
CODE_TO_TOKEN_ERROR,
REFRESH_TOKEN,
REFRESH_TOKEN_ERROR,
SOCIAL_LINK,
SOCIAL_LINK_ERROR,
REMOVE_SOCIAL_LINK,
REMOVE_SOCIAL_LINK_ERROR,
UPDATE_EMAIL,
UPDATE_EMAIL_ERROR,
UPDATE_PROFILE,
UPDATE_PROFILE_ERROR,
UPDATE_PASSWORD,
UPDATE_PASSWORD_ERROR,
UPDATE_TOTP,
UPDATE_TOTP_ERROR,
VERIFY_EMAIL,
VERIFY_EMAIL_ERROR,
REMOVE_TOTP,
REMOVE_TOTP_ERROR,
SEND_VERIFY_EMAIL,
SEND_VERIFY_EMAIL_ERROR,
SEND_RESET_PASSWORD,
SEND_RESET_PASSWORD_ERROR
}
@@ -0,0 +1,2 @@
org.keycloak.audit.AuditProviderSpi
org.keycloak.audit.AuditListenerSpi
+52
View File
@@ -0,0 +1,52 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-email</artifactId>
<name>Keycloak Audit Email Provider</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-email-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,57 @@
package org.keycloak.audit.email;
import org.jboss.logging.Logger;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventType;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class EmailAuditListener implements AuditListener {
private static final Logger log = Logger.getLogger(EmailAuditListener.class);
private KeycloakSession keycloakSession;
private EmailProvider emailProvider;
private Set<EventType> includedEvents;
public EmailAuditListener(KeycloakSession keycloakSession, EmailProvider emailProvider, Set<EventType> includedEvents) {
this.keycloakSession = keycloakSession;
this.emailProvider = emailProvider;
this.includedEvents = includedEvents;
}
@Override
public void onEvent(Event event) {
if (includedEvents.contains(event.getEvent())) {
if (event.getRealmId() != null && event.getUserId() != null) {
RealmModel realm = keycloakSession.getRealm(event.getRealmId());
UserModel user = realm.getUserById(event.getUserId());
if (user != null && user.getEmail() != null && user.isEmailVerified()) {
try {
emailProvider.setRealm(realm).setUser(user).sendEvent(event);
} catch (EmailException e) {
log.error("Failed to send event mail", e);
}
}
}
}
}
@Override
public void close() {
}
}
@@ -0,0 +1,62 @@
package org.keycloak.audit.email;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.audit.EventType;
import org.keycloak.email.EmailProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderSession;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class EmailAuditListenerFactory implements AuditListenerFactory {
private static final Set<EventType> SUPPORTED_EVENTS = new HashSet<EventType>();
static {
Collections.addAll(SUPPORTED_EVENTS, EventType.LOGIN_ERROR, EventType.UPDATE_PASSWORD, EventType.REMOVE_TOTP, EventType.UPDATE_TOTP);
}
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override
public AuditListener create(ProviderSession providerSession) {
KeycloakSession keycloakSession = providerSession.getProvider(KeycloakSession.class);
EmailProvider emailProvider = providerSession.getProvider(EmailProvider.class);
return new EmailAuditListener(keycloakSession, emailProvider, includedEvents);
}
@Override
public void init(Config.Scope config) {
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
includedEvents.add(EventType.valueOf(i.toUpperCase()));
}
} else {
includedEvents.addAll(SUPPORTED_EVENTS);
}
String[] exclude = config.getArray("exclude-events");
if (exclude != null) {
for (String e : exclude) {
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
}
}
}
@Override
public void close() {
}
@Override
public String getId() {
return "email";
}
}
@@ -0,0 +1 @@
org.keycloak.audit.email.EmailAuditListenerFactory
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-jboss-logging</artifactId>
<name>Keycloak Audit JBoss Logging Provider</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,66 @@
package org.keycloak.audit.log;
import org.jboss.logging.Logger;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.Event;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JBossLoggingAuditListener implements AuditListener {
private final Logger logger;
public JBossLoggingAuditListener(Logger logger) {
this.logger = logger;
}
@Override
public void onEvent(Event event) {
Logger.Level level = event.getError() != null ? Logger.Level.WARN : Logger.Level.INFO;
if (logger.isEnabled(level)) {
StringBuilder sb = new StringBuilder();
sb.append("event=");
sb.append(event.getEvent());
sb.append(", realmId=");
sb.append(event.getRealmId());
sb.append(", clientId=");
sb.append(event.getClientId());
sb.append(", userId=");
sb.append(event.getUserId());
sb.append(", ipAddress=");
sb.append(event.getIpAddress());
if (event.getError() != null) {
sb.append(", error=");
sb.append(event.getError());
}
if (event.getDetails() != null) {
for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
sb.append(", ");
sb.append(e.getKey());
if (e.getValue() == null || e.getValue().indexOf(' ') == -1) {
sb.append("=");
sb.append(e.getValue());
} else {
sb.append("='");
sb.append(e.getValue());
sb.append("'");
}
}
}
logger.log(level, sb.toString());
}
}
@Override
public void close() {
}
}
@@ -0,0 +1,36 @@
package org.keycloak.audit.log;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.provider.ProviderSession;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
public static final String ID = "jboss-logging";
private static final Logger logger = Logger.getLogger("org.keycloak.audit");
@Override
public AuditListener create(ProviderSession providerSession) {
return new JBossLoggingAuditListener(logger);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
@Override
public String getId() {
return ID;
}
}
@@ -0,0 +1 @@
org.keycloak.audit.log.JBossLoggingAuditListenerFactory
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-jpa</artifactId>
<name>Keycloak Audit JPA Provider</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-tests</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,115 @@
package org.keycloak.audit.jpa;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Entity
public class EventEntity {
@Id
private String id;
private long time;
private String event;
private String realmId;
private String clientId;
private String userId;
private String sessionId;
private String ipAddress;
private String error;
@Column(length = 2550)
private String detailsJson;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDetailsJson() {
return detailsJson;
}
public void setDetailsJson(String detailsJson) {
this.detailsJson = detailsJson;
}
}
@@ -0,0 +1,122 @@
package org.keycloak.audit.jpa;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.jboss.logging.Logger;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventQuery;
import org.keycloak.audit.EventType;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaAuditProvider implements AuditProvider {
private static final ObjectMapper mapper = new ObjectMapper();
private static final TypeReference<Map<String, String>> mapType = new TypeReference<Map<String, String>>() {
};
private static final Logger logger = Logger.getLogger(JpaAuditProvider.class);
private EntityManager em;
private EntityTransaction tx;
private Set<EventType> includedEvents;
public JpaAuditProvider(EntityManager em, Set<EventType> includedEvents) {
this.em = em;
this.includedEvents = includedEvents;
}
@Override
public EventQuery createQuery() {
return new JpaEventQuery(em);
}
@Override
public void clear() {
beginTx();
em.createQuery("delete from EventEntity").executeUpdate();
}
@Override
public void clear(String realmId) {
beginTx();
em.createQuery("delete from EventEntity where realmId = :realmId").setParameter("realmId", realmId).executeUpdate();
}
@Override
public void clear(String realmId, long olderThan) {
beginTx();
em.createQuery("delete from EventEntity where realmId = :realmId and time < :time").setParameter("realmId", realmId).setParameter("time", olderThan).executeUpdate();
}
@Override
public void onEvent(Event event) {
if (includedEvents.contains(event.getEvent())) {
beginTx();
em.persist(convert(event));
}
}
@Override
public void close() {
if (tx != null) {
tx.commit();
}
em.close();
}
private void beginTx() {
if (tx == null) {
tx = em.getTransaction();
tx.begin();
}
}
static EventEntity convert(Event o) {
EventEntity e = new EventEntity();
e.setId(UUID.randomUUID().toString());
e.setTime(o.getTime());
e.setEvent(o.getEvent().toString());
e.setRealmId(o.getRealmId());
e.setClientId(o.getClientId());
e.setUserId(o.getUserId());
e.setSessionId(o.getSessionId());
e.setIpAddress(o.getIpAddress());
e.setError(o.getError());
try {
e.setDetailsJson(mapper.writeValueAsString(o.getDetails()));
} catch (IOException ex) {
logger.error("Failed to write log details", ex);
}
return e;
}
static Event convert(EventEntity o) {
Event e = new Event();
e.setTime(o.getTime());
e.setEvent(EventType.valueOf(o.getEvent()));
e.setRealmId(o.getRealmId());
e.setClientId(o.getClientId());
e.setUserId(o.getUserId());
e.setSessionId(o.getSessionId());
e.setIpAddress(o.getIpAddress());
e.setError(o.getError());
try {
Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType);
e.setDetails(details);
} catch (IOException ex) {
logger.error("Failed to read log details", ex);
}
return e;
}
}
@@ -0,0 +1,62 @@
package org.keycloak.audit.jpa;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.EventType;
import org.keycloak.provider.ProviderSession;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaAuditProviderFactory implements AuditProviderFactory {
public static final String ID = "jpa";
private EntityManagerFactory emf;
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override
public AuditProvider create(ProviderSession providerSession) {
return new JpaAuditProvider(emf.createEntityManager(), includedEvents);
}
@Override
public void init(Config.Scope config) {
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store");
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
includedEvents.add(EventType.valueOf(i.toUpperCase()));
}
} else {
for (EventType i : EventType.values()) {
includedEvents.add(i);
}
}
String[] exclude = config.getArray("exclude-events");
if (exclude != null) {
for (String e : exclude) {
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
}
}
}
@Override
public void close() {
emf.close();
}
@Override
public String getId() {
return ID;
}
}
@@ -0,0 +1,111 @@
package org.keycloak.audit.jpa;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventQuery;
import org.keycloak.audit.EventType;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaEventQuery implements EventQuery {
private final EntityManager em;
private final CriteriaBuilder cb;
private final CriteriaQuery<EventEntity> cq;
private final Root<EventEntity> root;
private final ArrayList<Predicate> predicates;
private Integer firstResult;
private Integer maxResults;
public JpaEventQuery(EntityManager em) {
this.em = em;
cb = em.getCriteriaBuilder();
cq = cb.createQuery(EventEntity.class);
root = cq.from(EventEntity.class);
predicates = new ArrayList<Predicate>(4);
}
@Override
public EventQuery event(EventType... events) {
List<String> eventStrings = new LinkedList<String>();
for (EventType e : events) {
eventStrings.add(e.toString());
}
predicates.add(root.get("event").in(eventStrings));
return this;
}
@Override
public EventQuery realm(String realmId) {
predicates.add(cb.equal(root.get("realmId"), realmId));
return this;
}
@Override
public EventQuery client(String clientId) {
predicates.add(cb.equal(root.get("clientId"), clientId));
return this;
}
@Override
public EventQuery user(String userId) {
predicates.add(cb.equal(root.get("userId"), userId));
return this;
}
@Override
public EventQuery ipAddress(String ipAddress) {
predicates.add(cb.equal(root.get("ipAddress"), ipAddress));
return this;
}
@Override
public EventQuery firstResult(int firstResult) {
this.firstResult = firstResult;
return this;
}
@Override
public EventQuery maxResults(int maxResults) {
this.maxResults = maxResults;
return this;
}
@Override
public List<Event> getResultList() {
if (!predicates.isEmpty()) {
cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
}
cq.orderBy(cb.desc(root.get("time")));
TypedQuery<EventEntity> query = em.createQuery(cq);
if (firstResult != null) {
query.setFirstResult(firstResult);
}
if (maxResults != null) {
query.setMaxResults(maxResults);
}
List<Event> events = new LinkedList<Event>();
for (EventEntity e : query.getResultList()) {
events.add(JpaAuditProvider.convert(e));
}
return events;
}
}
@@ -0,0 +1 @@
org.keycloak.audit.jpa.JpaAuditProviderFactory
@@ -0,0 +1,15 @@
package org.keycloak.audit.jpa;
import org.keycloak.audit.tests.AbstractAuditProviderTest;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaAuditProviderTest extends AbstractAuditProviderTest {
@Override
protected String getProviderId() {
return JpaAuditProviderFactory.ID;
}
}
+22
View File
@@ -0,0 +1,22 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="jpa-keycloak-audit-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.keycloak.audit.jpa.EventEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
+136
View File
@@ -0,0 +1,136 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-mongo</artifactId>
<name>Keycloak Audit Mongo Provider</name>
<description/>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<keycloak.audit.mongo.host>localhost</keycloak.audit.mongo.host>
<keycloak.audit.mongo.port>27018</keycloak.audit.mongo.port>
<keycloak.audit.mongo.db>keycloak</keycloak.audit.mongo.db>
<keycloak.audit.mongo.clearOnStartup>true</keycloak.audit.mongo.clearOnStartup>
<keycloak.audit.mongo.bindIp>127.0.0.1</keycloak.audit.mongo.bindIp>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.audit.mongo.host>${keycloak.audit.mongo.host}</keycloak.audit.mongo.host>
<keycloak.audit.mongo.port>${keycloak.audit.mongo.port}</keycloak.audit.mongo.port>
<keycloak.audit.mongo.db>${keycloak.audit.mongo.db}</keycloak.audit.mongo.db>
<keycloak.audit.mongo.clearOnStartup>${keycloak.audit.mongo.clearOnStartup}</keycloak.audit.mongo.clearOnStartup>
<keycloak.audit.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.audit.mongo.bindIp>
</systemPropertyVariables>
</configuration>
</execution>
<execution>
<id>default-test</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<!-- Embedded mongo -->
<plugin>
<groupId>com.github.joelittlejohn.embedmongo</groupId>
<artifactId>embedmongo-maven-plugin</artifactId>
<executions>
<execution>
<id>start-mongodb</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.audit.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
<bindIp>${keycloak.audit.mongo.bindIp}</bindIp>
</configuration>
</execution>
<execution>
<id>stop-mongodb</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,107 @@
package org.keycloak.audit.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventQuery;
import org.keycloak.audit.EventType;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MongoAuditProvider implements AuditProvider {
private DBCollection audit;
private Set<EventType> includedEvents;
public MongoAuditProvider(DBCollection audit, Set<EventType> includedEvents) {
this.audit = audit;
this.includedEvents = includedEvents;
}
@Override
public EventQuery createQuery() {
return new MongoEventQuery(audit);
}
@Override
public void clear() {
audit.remove(new BasicDBObject());
}
@Override
public void clear(String realmId) {
audit.remove(new BasicDBObject("realmId", realmId));
}
@Override
public void clear(String realmId, long olderThan) {
BasicDBObject q = new BasicDBObject();
q.put("realmId", realmId);
q.put("time", new BasicDBObject("$lt", olderThan));
audit.remove(q);
}
@Override
public void onEvent(Event event) {
if (includedEvents.contains(event.getEvent())) {
audit.insert(convert(event));
}
}
@Override
public void close() {
}
static DBObject convert(Event o) {
BasicDBObject e = new BasicDBObject();
e.put("time", o.getTime());
e.put("event", o.getEvent().toString());
e.put("realmId", o.getRealmId());
e.put("clientId", o.getClientId());
e.put("userId", o.getUserId());
e.put("sessionId", o.getSessionId());
e.put("ipAddress", o.getIpAddress());
e.put("error", o.getError());
BasicDBObject details = new BasicDBObject();
if (o.getDetails() != null) {
for (Map.Entry<String, String> entry : o.getDetails().entrySet()) {
details.put(entry.getKey(), entry.getValue());
}
}
e.put("details", details);
return e;
}
static Event convert(BasicDBObject o) {
Event e = new Event();
e.setTime(o.getLong("time"));
e.setEvent(EventType.valueOf(o.getString("event")));
e.setRealmId(o.getString("realmId"));
e.setClientId(o.getString("clientId"));
e.setUserId(o.getString("userId"));
e.setSessionId(o.getString("sessionId"));
e.setIpAddress(o.getString("ipAddress"));
e.setError(o.getString("error"));
BasicDBObject d = (BasicDBObject) o.get("details");
if (d != null) {
Map<String, String> details = new HashMap<String, String>();
for (Object k : d.keySet()) {
details.put((String) k, d.getString((String) k));
}
e.setDetails(details);
}
return e;
}
}
@@ -0,0 +1,96 @@
package org.keycloak.audit.mongo;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.EventType;
import org.keycloak.provider.ProviderSession;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MongoAuditProviderFactory implements AuditProviderFactory {
protected static final Logger logger = Logger.getLogger(MongoAuditProviderFactory.class);
public static final String ID = "mongo";
private MongoClient client;
private DB db;
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override
public AuditProvider create(ProviderSession providerSession) {
return new MongoAuditProvider(db.getCollection("audit"), includedEvents);
}
@Override
public void init(Config.Scope config) {
try {
String host = config.get("host", ServerAddress.defaultHost());
int port = config.getInt("port", ServerAddress.defaultPort());
String dbName = config.get("db", "keycloak-audit");
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
String user = config.get("user");
String password = config.get("password");
if (user != null && password != null) {
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
} else {
client = new MongoClient(host, port);
}
client.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
db = client.getDB(dbName);
if (clearOnStartup) {
db.getCollection("audit").drop();
}
logger.infof("Initialized mongo audit. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
includedEvents.add(EventType.valueOf(i.toUpperCase()));
}
} else {
for (EventType i : EventType.values()) {
includedEvents.add(i);
}
}
String[] exclude = config.getArray("exclude-events");
if (exclude != null) {
for (String e : exclude) {
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
}
}
}
@Override
public void close() {
client.close();
}
@Override
public String getId() {
return ID;
}
}
@@ -0,0 +1,93 @@
package org.keycloak.audit.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventQuery;
import org.keycloak.audit.EventType;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MongoEventQuery implements EventQuery {
private Integer firstResult;
private Integer maxResults;
private DBCollection audit;
private final BasicDBObject query;
public MongoEventQuery(DBCollection audit) {
this.audit = audit;
query = new BasicDBObject();
}
@Override
public EventQuery event(EventType... events) {
List<String> eventStrings = new LinkedList<String>();
for (EventType e : events) {
eventStrings.add(e.toString());
}
query.put("event", new BasicDBObject("$in", eventStrings));
return this;
}
@Override
public EventQuery realm(String realmId) {
query.put("realmId", realmId);
return this;
}
@Override
public EventQuery client(String clientId) {
query.put("clientId", clientId);
return this;
}
@Override
public EventQuery user(String userId) {
query.put("userId", userId);
return this;
}
@Override
public EventQuery ipAddress(String ipAddress) {
query.put("ipAddress", ipAddress);
return this;
}
@Override
public EventQuery firstResult(int firstResult) {
this.firstResult = firstResult;
return this;
}
@Override
public EventQuery maxResults(int maxResults) {
this.maxResults = maxResults;
return this;
}
@Override
public List<Event> getResultList() {
DBCursor cur = audit.find(query).sort(new BasicDBObject("time", -1));
if (firstResult != null) {
cur.skip(firstResult);
}
if (maxResults != null) {
cur.limit(maxResults);
}
List<Event> events = new LinkedList<Event>();
while (cur.hasNext()) {
events.add(MongoAuditProvider.convert((BasicDBObject) cur.next()));
}
return events;
}
}
@@ -0,0 +1 @@
org.keycloak.audit.mongo.MongoAuditProviderFactory
@@ -0,0 +1,15 @@
package org.keycloak.audit.mongo;
import org.keycloak.audit.tests.AbstractAuditProviderTest;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MongoAuditProviderTest extends AbstractAuditProviderTest {
@Override
protected String getProviderId() {
return MongoAuditProviderFactory.ID;
}
}
Executable
+26
View File
@@ -0,0 +1,26 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Audit Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-parent</artifactId>
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>email</module>
<module>jpa</module>
<module>jboss-logging</module>
<module>mongo</module>
<module>tests</module>
</modules>
</project>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-audit-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-audit-tests</artifactId>
<name>Keycloak Audit Tests</name>
<description/>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,139 @@
package org.keycloak.audit.tests;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.Event;
import org.keycloak.audit.EventType;
import org.keycloak.provider.ProviderFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class AbstractAuditProviderTest {
private ProviderFactory<AuditProvider> factory;
private AuditProvider provider;
@Before
public void before() {
String providerId = getProviderId();
ServiceLoader<AuditProviderFactory> factories = ServiceLoader.load(AuditProviderFactory.class);
for (AuditProviderFactory f : factories) {
if (f.getId().equals(providerId)) {
factory = f;
factory.init(Config.scope("audit", providerId));
}
}
provider = factory.create(null);
}
protected abstract String getProviderId();
@After
public void after() {
provider.clear();
provider.close();
factory.close();
}
@Test
public void save() {
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
}
@Test
public void query() {
long oldest = System.currentTimeMillis() - 30000;
long newest = System.currentTimeMillis() + 30000;
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
provider.onEvent(create(EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(oldest, EventType.LOGIN, "realmId", "clientId2", "userId", "127.0.0.1", "error"));
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
provider.close();
provider = factory.create(null);
Assert.assertEquals(5, provider.createQuery().client("clientId").getResultList().size());
Assert.assertEquals(5, provider.createQuery().realm("realmId").getResultList().size());
Assert.assertEquals(4, provider.createQuery().event(EventType.LOGIN).getResultList().size());
Assert.assertEquals(6, provider.createQuery().event(EventType.LOGIN, EventType.REGISTER).getResultList().size());
Assert.assertEquals(4, provider.createQuery().user("userId").getResultList().size());
Assert.assertEquals(1, provider.createQuery().user("userId").event(EventType.REGISTER).getResultList().size());
Assert.assertEquals(2, provider.createQuery().maxResults(2).getResultList().size());
Assert.assertEquals(1, provider.createQuery().firstResult(5).getResultList().size());
Assert.assertEquals(newest, provider.createQuery().maxResults(1).getResultList().get(0).getTime());
Assert.assertEquals(oldest, provider.createQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime());
}
@Test
public void clear() {
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
provider.close();
provider = factory.create(null);
provider.clear("realmId");
Assert.assertEquals(1, provider.createQuery().getResultList().size());
}
@Test
public void clearOld() {
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
provider.close();
provider = factory.create(null);
provider.clear("realmId", System.currentTimeMillis() - 10000);
Assert.assertEquals(3, provider.createQuery().getResultList().size());
}
private Event create(EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
return create(System.currentTimeMillis(), event, realmId, clientId, userId, ipAddress, error);
}
private Event create(long time, EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
Event e = new Event();
e.setTime(time);
e.setEvent(event);
e.setRealmId(realmId);
e.setClientId(clientId);
e.setUserId(userId);
e.setIpAddress(ipAddress);
e.setError(error);
Map<String, String> details = new HashMap<String, String>();
details.put("key1", "value1");
details.put("key2", "value2");
e.setDetails(details);
return e;
}
}
+48
View File
@@ -0,0 +1,48 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-authentication-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-authentication-api</artifactId>
<name>Keycloak Authentication SPI</name>
<description />
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,15 @@
package org.keycloak.authentication;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AuthProviderConstants {
// Model is default provider. See AuthenticationProviderModel.DEFAULT_PROVIDER
public static final String PROVIDER_NAME_MODEL = "model";
public static final String PROVIDER_NAME_EXTERNAL_MODEL = "externalModel";
public static final String PROVIDER_NAME_PICKETLINK = "picketlink";
// Used in external-model provider
public static final String EXTERNAL_REALM_ID = "externalRealmId";
}
@@ -0,0 +1,12 @@
package org.keycloak.authentication;
/**
* Result of authentication by AuthenticationProvider
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public enum AuthProviderStatus {
SUCCESS, INVALID_CREDENTIALS, FAILED
}
@@ -0,0 +1,83 @@
package org.keycloak.authentication;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AuthUser {
private String id;
private String username;
private String firstName;
private String lastName;
private String email;
private String providerName;
public AuthUser(String id, String username, String providerName) {
this.id = id;
this.username = username;
this.providerName = providerName;
}
public String getId() {
return id;
}
public AuthUser setId(String id) {
this.id = id;
return this;
}
public String getUsername() {
return username;
}
public AuthUser setUsername(String username) {
this.username = username;
return this;
}
public String getFirstName() {
return firstName;
}
public AuthUser setName(String name) {
int i = name.lastIndexOf(' ');
if (i != -1) {
firstName = name.substring(0, i);
lastName = name.substring(i + 1);
} else {
firstName = name;
}
return this;
}
public AuthUser setName(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
return this;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public AuthUser setEmail(String email) {
this.email = email;
return this;
}
public String getProviderName() {
return providerName;
}
public AuthUser setProviderName(String providerName) {
this.providerName = providerName;
return this;
}
}
@@ -0,0 +1,68 @@
package org.keycloak.authentication;
import java.util.List;
import java.util.Map;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface AuthenticationProvider extends Provider {
String getName();
/**
* Get names of all available configuration options of current provider
*
* @return options or empty list if no options available
*/
List<String> getAvailableOptions();
/**
* Get user by given username or email. Return user instance or null if user doesn't exists in this authentication provider
*
* @param realm
* @param configuration
* @param username or email
* @return found user or null if user with given username doesn't exists
* @throws AuthenticationProviderException
*/
AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
/**
* Try to register user with this authentication provider
*
* @param realm
* @param configuration
* @param username
* @return ID of newly created user (For example ID from LDAP)
* @throws AuthenticationProviderException if user creation couldn't happen
*/
String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
/**
* Standard Authentication flow
*
* @param username
* @param password
* @return result of authentication, which might eventually encapsulate info about authenticated user and provider which successfully authenticated
*/
AuthProviderStatus validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException;
/**
* Update credential
*
* @param realm
* @param configuration
* @param username
* @param password
* @return true if credential has been successfully updated
* @throws AuthenticationProviderException
*/
boolean updateCredential(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException;
}
@@ -0,0 +1,24 @@
package org.keycloak.authentication;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AuthenticationProviderException extends Exception {
private static final long serialVersionUID = 15L;
protected AuthenticationProviderException() {
}
public AuthenticationProviderException(String message) {
super(message);
}
public AuthenticationProviderException(Throwable cause) {
super(cause);
}
public AuthenticationProviderException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,9 @@
package org.keycloak.authentication;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface AuthenticationProviderFactory extends ProviderFactory<AuthenticationProvider> {
}
@@ -0,0 +1,219 @@
package org.keycloak.authentication;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.util.ProviderLoader;
/**
* Access point to authentication SPI. It finds configured and available {@link AuthenticationProvider} instances for current realm
* and then delegates method call to them.
*
* Example of usage: AuthenticationProviderManager.getManager(realm).validateUser("joe", "password");
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AuthenticationProviderManager {
private static final Logger logger = Logger.getLogger(AuthenticationProviderManager.class);
private final RealmModel realm;
private final Map<String, AuthenticationProvider> delegates;
public static AuthenticationProviderManager getManager(RealmModel realm, ProviderSession providerSession) {
Iterable<AuthenticationProvider> providers = providerSession.getAllProviders(AuthenticationProvider.class);
Map<String, AuthenticationProvider> providersMap = new HashMap<String, AuthenticationProvider>();
for (AuthenticationProvider provider : providers) {
providersMap.put(provider.getName(), provider);
}
return new AuthenticationProviderManager(realm, providersMap);
}
public AuthenticationProviderManager(RealmModel realm, Map<String, AuthenticationProvider> delegates) {
this.realm = realm;
this.delegates = delegates;
}
public AuthUser getUser(String username) {
List<AuthenticationProviderModel> authProviderModels = getConfiguredProviderModels(realm);
for (AuthenticationProviderModel providerModel : authProviderModels) {
AuthenticationProvider delegate = getProvider(providerModel.getProviderName());
if (delegate == null) {
continue;
}
try {
AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), username);
if (authUser != null) {
logger.debugf("User '%s' found with provider '%s'", username, providerModel.getProviderName());
return authUser;
}
} catch (AuthenticationProviderException ape) {
logger.warn(ape.getMessage(), ape);
}
}
logger.debugf("User '%s' not found with any provider", username);
return null;
}
public AuthProviderStatus validatePassword(UserModel user, String password) {
AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
if (authLink == null) {
// User not yet linked with any authenticationProvider. Find provider with biggest priority where he is and link
AuthUser authUser = getUser(user.getLoginName());
authLink = new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId());
realm.setAuthenticationLink(user, authLink);
logger.infof("User '%s' linked with provider '%s'", authUser.getUsername(), authUser.getProviderName());
}
String providerName = authLink.getAuthProvider();
AuthenticationProviderModel providerModel = getConfiguredProviderModel(realm, providerName);
AuthenticationProvider delegate = getProvider(providerName);
if (delegate == null || providerModel == null) {
return AuthProviderStatus.FAILED;
}
try {
checkCorrectAuthLink(delegate, providerModel, authLink, user.getLoginName());
AuthProviderStatus currentResult = delegate.validatePassword(realm, providerModel.getConfig(), user.getLoginName(), password);
logger.debugf("Authentication provider '%s' finished with '%s' for authentication of '%s'", delegate.getName(), currentResult.toString(), user.getLoginName());
return currentResult;
} catch (AuthenticationProviderException ape) {
logger.warn(ape.getMessage(), ape);
return AuthProviderStatus.FAILED;
}
}
public boolean updatePassword(UserModel user, String password) throws AuthenticationProviderException {
AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
if (authLink == null) {
// Find provider with biggest priority where password update is supported. Then register user here and link him
List<AuthenticationProviderModel> configuredProviders = getConfiguredProviderModels(realm);
for (AuthenticationProviderModel providerModel : configuredProviders) {
if (providerModel.isPasswordUpdateSupported()) {
AuthenticationProvider delegate = getProvider(providerModel.getProviderName());
if (delegate != null) {
AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), user.getLoginName());
if (authUser != null) {
// Linking existing user supported just for "model" provider. In other cases throw exception
if (providerModel.getProviderName().equals(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName())) {
authLink = new AuthenticationLinkModel(providerModel.getProviderName(), authUser.getId());
realm.setAuthenticationLink(user, authLink);
logger.infof("User '%s' linked with provider '%s'", authUser.getUsername(), authUser.getProviderName());
} else {
throw new AuthenticationProviderException("User " + authUser.getUsername() + " exists in provider "
+ authUser.getProviderName() + " but is not linked with model user");
}
} else {
String userIdInProvider = delegate.registerUser(realm, providerModel.getConfig(), user.getLoginName());
authLink = new AuthenticationLinkModel(providerModel.getProviderName(), userIdInProvider);
realm.setAuthenticationLink(user, authLink);
logger.infof("User '%s' registered in provider '%s' and linked", user.getLoginName(), providerModel.getProviderName());
}
break;
}
}
}
if (authLink == null) {
logger.warnf("No providers found where password update is supported for user '%s'", user.getLoginName());
return false;
}
}
String providerName = authLink.getAuthProvider();
AuthenticationProviderModel providerModel = getConfiguredProviderModel(realm, providerName);
if (providerModel == null) {
return false;
}
String username = user.getLoginName();
// Update just those, which support password update
if (providerModel.isPasswordUpdateSupported()) {
try {
AuthenticationProvider delegate = getProvider(providerName);
if (delegate == null) {
return false;
}
checkCorrectAuthLink(delegate, providerModel, authLink, username);
if (delegate.updateCredential(realm,providerModel.getConfig(), user.getLoginName(), password)) {
logger.debugf("Updated password in authentication provider '%s' for user '%s'", providerName, username);
return true;
} else {
logger.warnf("Password not updated in authentication provider '%s' for user '%s'", providerName, username);
return false;
}
} catch (AuthenticationProviderException ape) {
// Rethrow it to upper layer
logger.warn("Failed to update password: " + ape.getMessage());
throw ape;
}
} else {
logger.warnf("Skip password update for authentication provider '%s' for user '%s'", providerName, username);
return false;
}
}
private AuthenticationProvider getProvider(String providerName) {
AuthenticationProvider delegate = delegates.get(providerName);
if (delegate == null) {
logger.warnf("Provider '%s' not available on classpath", providerName);
}
return delegate;
}
private static List<AuthenticationProviderModel> getConfiguredProviderModels(RealmModel realm) {
List<AuthenticationProviderModel> configuredProviders = realm.getAuthenticationProviders();
// Use model based authentication of current realm by default
if (configuredProviders == null || configuredProviders.isEmpty()) {
configuredProviders = Collections.EMPTY_LIST;
logger.warnf("No authentication providers found");
}
return configuredProviders;
}
public static AuthenticationProviderModel getConfiguredProviderModel(RealmModel realm, String providerName) {
List<AuthenticationProviderModel> providers = getConfiguredProviderModels(realm);
for (AuthenticationProviderModel provider : providers) {
if (providerName.equals(provider.getProviderName())) {
return provider;
}
}
logger.warnf("Provider '%s' not configured in realm", providerName);
return null;
}
// Check if ID of linked AuthUser is same as expected ID from authenticationLink . It should catch the case when for example user "john" was deleted in LDAP
// and then user "john" has been created again, but it's actually different user with different ID
private void checkCorrectAuthLink(AuthenticationProvider authProvider, AuthenticationProviderModel providerModel,
AuthenticationLinkModel authLinkModel, String username) throws AuthenticationProviderException {
AuthUser authUser = authProvider.getUser(realm, providerModel.getConfig(), username);
if (authUser == null) {
throw new AuthenticationProviderException("User " + username + " not found in authentication provider " + providerModel.getProviderName());
}
String userExternalId = authUser.getId();
if (!userExternalId.equals(authLinkModel.getAuthUserId())) {
throw new AuthenticationProviderException("ID did not match! ID from provider: " + userExternalId + ", ID from authentication link: " + authLinkModel.getAuthUserId());
}
}
}
@@ -0,0 +1,27 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuthenticationSpi implements Spi {
@Override
public String getName() {
return "authentication";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuthenticationProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuthenticationProviderFactory.class;
}
}
@@ -0,0 +1 @@
org.keycloak.authentication.AuthenticationSpi
+74
View File
@@ -0,0 +1,74 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-authentication-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-authentication-model</artifactId>
<name>Keycloak Authentication Model Based</name>
<description />
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authentication-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,84 @@
package org.keycloak.authentication.model;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.authentication.AuthProviderStatus;
import org.keycloak.authentication.AuthUser;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderException;
/**
* Authentication provider, which delegates calling of all methods to specified realm
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractModelAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = Logger.getLogger(AbstractModelAuthenticationProvider.class);
@Override
public AuthUser getUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
return user == null ? null : createAuthenticatedUserInstance(user);
}
@Override
public String registerUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
UserModel user = currentRealm.addUser(username);
user.setEnabled(true);
return user.getId();
}
@Override
public AuthProviderStatus validatePassword(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
boolean result = realm.validatePassword(user, password);
return result ? AuthProviderStatus.SUCCESS : AuthProviderStatus.INVALID_CREDENTIALS;
}
@Override
public boolean updateCredential(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
// Validate password policy
String error = realm.getPasswordPolicy().validate(password);
if (error != null) {
throw new AuthenticationProviderException(error);
}
UserModel user = realm.getUser(username);
if (user == null) {
logger.warnf("User '%s' doesn't exists. Skip password update", username);
return false;
}
UserCredentialModel cred = new UserCredentialModel();
cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue(password);
realm.updateCredential(user, cred);
return true;
}
@Override
public void close() {
}
protected abstract RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) throws AuthenticationProviderException;
protected AuthUser createAuthenticatedUserInstance(UserModel user) {
return new AuthUser(user.getId(), user.getLoginName(), getName())
.setName(user.getFirstName(), user.getLastName())
.setEmail(user.getEmail());
}
}
@@ -0,0 +1,53 @@
package org.keycloak.authentication.model;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProviderException;
import org.keycloak.provider.ProviderSession;
/**
* AbstractModelAuthenticationProvider, which delegates authentication operations to different (external) realm
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ExternalModelAuthenticationProvider extends AbstractModelAuthenticationProvider {
public ExternalModelAuthenticationProvider(ProviderSession providerSession) {
}
@Override
public String getName() {
return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL;
}
@Override
public List<String> getAvailableOptions() {
return Arrays.asList(AuthProviderConstants.EXTERNAL_REALM_ID);
}
@Override
public RealmModel getRealm(RealmModel currentRealm, Map<String, String> configuration) throws AuthenticationProviderException {
String realmId = configuration.get(AuthProviderConstants.EXTERNAL_REALM_ID);
if (realmId == null) {
throw new AuthenticationProviderException("Option '" + AuthProviderConstants.EXTERNAL_REALM_ID + "' not specified in configuration");
}
// TODO: This won't be needed when KeycloakSession is available from ProviderSession
KeycloakSession session = ResteasyProviderFactory.getContextData(KeycloakSession.class);
if (session == null) {
throw new AuthenticationProviderException("KeycloakSession not available");
}
RealmModel realm = session.getRealm(realmId);
if (realm == null) {
throw new AuthenticationProviderException("Realm with id '" + realmId + "' doesn't exists");
}
return realm;
}
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication.model;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.provider.ProviderSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ExternalModelAuthenticationProviderFactory implements AuthenticationProviderFactory {
@Override
public AuthenticationProvider create(ProviderSession providerSession) {
return new ExternalModelAuthenticationProvider(providerSession);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
@Override
public String getId() {
return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL;
}
}
@@ -0,0 +1,31 @@
package org.keycloak.authentication.model;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.keycloak.models.RealmModel;
import org.keycloak.authentication.AuthProviderConstants;
/**
* AbstractModelAuthenticationProvider, which uses current realm to call operations on
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ModelAuthenticationProvider extends AbstractModelAuthenticationProvider {
@Override
public String getName() {
return AuthProviderConstants.PROVIDER_NAME_MODEL;
}
@Override
public List<String> getAvailableOptions() {
return Collections.EMPTY_LIST;
}
@Override
protected RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) {
return currentRealm;
}
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication.model;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.provider.ProviderSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ModelAuthenticationProviderFactory implements AuthenticationProviderFactory {
@Override
public AuthenticationProvider create(ProviderSession providerSession) {
return new ModelAuthenticationProvider();
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
@Override
public String getId() {
return AuthProviderConstants.PROVIDER_NAME_MODEL;
}
}
@@ -0,0 +1,2 @@
org.keycloak.authentication.model.ModelAuthenticationProviderFactory
org.keycloak.authentication.model.ExternalModelAuthenticationProviderFactory
+101
View File
@@ -0,0 +1,101 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-authentication-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-authentication-picketlink</artifactId>
<name>Keycloak Authentication Picketlink Based</name>
<description />
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authentication-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-picketlink-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,130 @@
package org.keycloak.authentication.picketlink;
import org.jboss.logging.Logger;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthProviderStatus;
import org.keycloak.authentication.AuthUser;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderException;
import org.keycloak.models.RealmModel;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* AuthenticationProvider, which delegates authentication to picketlink
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PicketlinkAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = Logger.getLogger(PicketlinkAuthenticationProvider.class);
private final IdentityManagerProvider identityManagerProvider;
public PicketlinkAuthenticationProvider(IdentityManagerProvider identityManagerProvider) {
this.identityManagerProvider = identityManagerProvider;
}
@Override
public String getName() {
return AuthProviderConstants.PROVIDER_NAME_PICKETLINK;
}
@Override
public List<String> getAvailableOptions() {
return Collections.EMPTY_LIST;
}
@Override
public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
try {
User picketlinkUser = BasicModel.getUser(identityManager, username);
return picketlinkUser == null ? null : new AuthUser(picketlinkUser.getId(), picketlinkUser.getLoginName(), getName())
.setName(picketlinkUser.getFirstName(), picketlinkUser.getLastName())
.setEmail(picketlinkUser.getEmail())
.setProviderName(getName());
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
}
@Override
public String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
try {
User picketlinkUser = new User(username);
identityManager.add(picketlinkUser);
return picketlinkUser.getId();
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
}
@Override
public AuthProviderStatus validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
try {
UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
credential.setUsername(username);
credential.setPassword(new Password(password.toCharArray()));
identityManager.validateCredentials(credential);
if (credential.getStatus() == Credentials.Status.VALID) {
return AuthProviderStatus.SUCCESS;
} else {
return AuthProviderStatus.INVALID_CREDENTIALS;
}
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
}
@Override
public boolean updateCredential(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
try {
User picketlinkUser = BasicModel.getUser(identityManager, username);
if (picketlinkUser == null) {
logger.debugf("User '%s' doesn't exists. Skip password update", username);
return false;
}
identityManager.updateCredential(picketlinkUser, new Password(password.toCharArray()));
return true;
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
}
@Override
public void close() {
}
public IdentityManager getIdentityManager(RealmModel realm) throws AuthenticationProviderException {
return identityManagerProvider.getIdentityManager(realm);
}
private AuthenticationProviderException convertIDMException(IdentityManagementException ie) {
Throwable realCause = ie;
while (realCause.getCause() != null) {
realCause = realCause.getCause();
}
// Use the message from the realCause
return new AuthenticationProviderException(realCause.getMessage(), ie);
}
}
@@ -0,0 +1,33 @@
package org.keycloak.authentication.picketlink;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.keycloak.provider.ProviderSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PicketlinkAuthenticationProviderFactory implements AuthenticationProviderFactory {
@Override
public AuthenticationProvider create(ProviderSession providerSession) {
return new PicketlinkAuthenticationProvider(providerSession.getProvider(IdentityManagerProvider.class));
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
@Override
public String getId() {
return AuthProviderConstants.PROVIDER_NAME_PICKETLINK;
}
}
@@ -0,0 +1 @@
org.keycloak.authentication.picketlink.PicketlinkAuthenticationProviderFactory
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<artifactId>keycloak-authentication-parent</artifactId>
<name>Keycloak Authentication</name>
<description />
<modules>
<module>authentication-api</module>
<module>authentication-model</module>
<module>authentication-picketlink</module>
</modules>
</project>
+5 -9
View File
@@ -1,9 +1,10 @@
<?xml version="1.0"?>
<project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,11 +19,6 @@
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
@@ -61,8 +57,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
@@ -18,11 +18,11 @@ public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
protected ObjectMapper mapper = new ObjectMapper();
public SkeletonKeyContextResolver() {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public SkeletonKeyContextResolver(boolean indent) {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
if (indent) {
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
}
+5 -4
View File
@@ -1,9 +1,10 @@
<?xml version="1.0"?>
<project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-beta-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -45,8 +46,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
@@ -2,7 +2,6 @@ package org.keycloak;
import org.keycloak.util.KeycloakUriBuilder;
import java.security.KeyStore;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@@ -12,18 +11,19 @@ import java.util.concurrent.atomic.AtomicLong;
* @version $Revision: 1 $
*/
public class AbstractOAuthClient {
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
private static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
private final AtomicLong counter = new AtomicLong();
protected String clientId;
protected Map<String, String> credentials;
protected KeyStore truststore;
protected String authUrl;
protected String codeUrl;
protected String refreshUrl;
protected String scope;
protected String stateCookieName = OAUTH_TOKEN_REQUEST_STATE;
protected String stateCookiePath;
protected boolean isSecure;
protected final AtomicLong counter = new AtomicLong();
protected boolean publicClient;
protected String getStateCode() {
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
}
@@ -44,20 +44,10 @@ public class AbstractOAuthClient {
this.credentials = credentials;
}
public KeyStore getTruststore() {
return truststore;
}
public void setTruststore(KeyStore truststore) {
this.truststore = truststore;
}
public String getAuthUrl() {
return authUrl;
}
public void setAuthUrl(String authUrl) {
this.authUrl = authUrl;
}
@@ -70,6 +60,14 @@ public class AbstractOAuthClient {
this.codeUrl = codeUrl;
}
public String getRefreshUrl() {
return refreshUrl;
}
public void setRefreshUrl(String refreshUrl) {
this.refreshUrl = refreshUrl;
}
public String getScope() {
return scope;
}
@@ -94,10 +92,18 @@ public class AbstractOAuthClient {
this.stateCookiePath = stateCookiePath;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
protected String stripOauthParametersFromRedirect(String uri) {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
.replaceQueryParam("code", null)
.replaceQueryParam("state", null);
.replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null);
return builder.build().toString();
}
+145
View File
@@ -0,0 +1,145 @@
package org.keycloak;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Config {
private static ConfigProvider configProvider = new SystemPropertiesConfigProvider();
public static void init(ConfigProvider configProvider) {
Config.configProvider = configProvider;
}
public static String getAdminRealm() {
return configProvider.scope("admin").get("realm", "master");
}
public static String getProvider(String spi) {
return configProvider.getProvider(spi);
}
public static Scope scope(String... scope) {
return configProvider.scope(scope);
}
public static interface ConfigProvider {
String getProvider(String spi);
Scope scope(String... scope);
}
public static class SystemPropertiesConfigProvider implements ConfigProvider {
@Override
public String getProvider(String spi) {
return System.getProperties().getProperty("keycloak." + spi + ".provider");
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append("keycloak.");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
public static class SystemPropertiesScope implements Scope {
private String prefix;
public SystemPropertiesScope(String prefix) {
this.prefix = prefix;
}
@Override
public String get(String key) {
return get(key, null);
}
@Override
public String get(String key, String defaultValue) {
return System.getProperty(prefix + key, defaultValue);
}
@Override
public String[] getArray(String key) {
String value = get(key);
if (value != null) {
String[] a = value.split(",");
for (int i = 0; i < a.length; i++) {
a[i] = a[i].trim();
}
return a;
} else {
return null;
}
}
@Override
public Integer getInt(String key) {
return getInt(key, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
String v = get(key, null);
return v != null ? Integer.parseInt(v) : defaultValue;
}
@Override
public Long getLong(String key) {
return getLong(key, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
String v = get(key, null);
return v != null ? Long.parseLong(v) : defaultValue;
}
@Override
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
String v = get(key, null);
return v != null ? Boolean.parseBoolean(v) : defaultValue;
}
}
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public static interface Scope {
String get(String key);
String get(String key, String defaultValue);
String[] getArray(String key);
Integer getInt(String key);
Integer getInt(String key, Integer defaultValue);
Long getLong(String key);
Long getLong(String key, Long defaultValue);
Boolean getBoolean(String key);
Boolean getBoolean(String key, Boolean defaultValue);
}
}
+49
View File
@@ -0,0 +1,49 @@
package org.keycloak;
import java.io.Serializable;
import java.security.Principal;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakPrincipal implements Principal, Serializable {
protected final String name;
protected final KeycloakSecurityContext context;
public KeycloakPrincipal(String name, KeycloakSecurityContext context) {
this.name = name;
this.context = context;
}
public KeycloakSecurityContext getKeycloakSecurityContext() {
return context;
}
@Override
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeycloakPrincipal that = (KeycloakPrincipal) o;
if (!name.equals(that.name)) return false;
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,43 @@
package org.keycloak;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSecurityContext implements Serializable {
protected String tokenString;
protected AccessToken token;
protected IDToken idToken;
protected String idTokenString;
public KeycloakSecurityContext() {
}
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken) {
this.tokenString = tokenString;
this.token = token;
this.idToken = idToken;
this.idTokenString = idTokenString;
}
public AccessToken getToken() {
return token;
}
public String getTokenString() {
return tokenString;
}
public IDToken getIdToken() {
return idToken;
}
public String getIdTokenString() {
return idTokenString;
}
}
@@ -0,0 +1,30 @@
package org.keycloak;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface OAuth2Constants {
String CODE = "code";
String CLIENT_ID = "client_id";
String ERROR = "error";
String ERROR_DESCRIPTION = "error_description";
String REDIRECT_URI = "redirect_uri";
String SCOPE = "scope";
String STATE = "state";
String GRANT_TYPE = "grant_type";
String RESPONSE_TYPE = "response_type";
String REFRESH_TOKEN = "refresh_token";
}
+64
View File
@@ -0,0 +1,64 @@
package org.keycloak;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthErrorException extends Exception {
public static final String INVALID_REQUEST = "invalid_request";
public static final String INVALID_CLIENT = "invalid_client";
public static final String INVALID_GRANT = "invalid_grant";
public static final String INVALID_SCOPE = "invalid_grant";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
public OAuthErrorException(String error, String description, String message, Throwable cause) {
super(message, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, String message) {
super(message);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description) {
super(description);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, Throwable cause) {
super(description, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error) {
super(error);
this.error = error;
}
public OAuthErrorException(String error, Throwable cause) {
super(error, cause);
this.error = error;
}
protected String error;
protected String description;
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@@ -2,7 +2,7 @@ package org.keycloak;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.AccessToken;
import java.io.IOException;
import java.security.PublicKey;
@@ -12,34 +12,40 @@ import java.security.PublicKey;
* @version $Revision: 1 $
*/
public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realm) throws VerificationException {
return verifyToken(tokenString, realmKey, realm, true);
}
public static SkeletonKeyToken verifyToken(String tokenString, PublicKey realmKey, String realm) throws VerificationException {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realm, boolean checkActive) throws VerificationException {
JWSInput input = new JWSInput(tokenString);
boolean verified = false;
try {
verified = RSAProvider.verify(input, realmKey);
} catch (Exception ignore) {
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
}
if (!verified) throw new VerificationException("Token signature not validated");
SkeletonKeyToken token = null;
AccessToken token;
try {
token = input.readJsonContent(SkeletonKeyToken.class);
token = input.readJsonContent(AccessToken.class);
} catch (IOException e) {
throw new VerificationException(e);
}
if (!token.isActive()) {
throw new VerificationException("Token is not active.");
throw new VerificationException("Couldn't parse token signature", e);
}
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null");
throw new VerificationException("Token user was null.");
}
if (!realm.equals(token.getAudience())) {
throw new VerificationException("Token audience doesn't match domain");
throw new VerificationException("Token audience doesn't match domain.");
}
if (checkActive && !token.isActive()) {
throw new VerificationException("Token is not active.");
}
return token;
}
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try {
return RSAProvider.verify(input, realmKey);
} catch (Exception e) {
throw new VerificationException("Token signature not validated.", e);
}
}
}
@@ -6,6 +6,12 @@ package org.keycloak;
*/
public interface ServiceUrlConstants {
public static final String TOKEN_SERVICE_LOGIN_PATH = "/rest/realms/{realm-name}/tokens/login";
public static final String TOKEN_SERVICE_ACCESS_CODE_PATH = "/rest/realms/{realm-name}/tokens/access/codes";
public static final String TOKEN_SERVICE_LOGIN_PATH = "/realms/{realm-name}/tokens/login";
public static final String TOKEN_SERVICE_ACCESS_CODE_PATH = "/realms/{realm-name}/tokens/access/codes";
public static final String TOKEN_SERVICE_REFRESH_PATH = "/realms/{realm-name}/tokens/refresh";
public static final String TOKEN_SERVICE_LOGOUT_PATH = "/realms/{realm-name}/tokens/logout";
public static final String TOKEN_SERVICE_DIRECT_GRANT_PATH = "/realms/{realm-name}/tokens/grants/access";
public static final String ACCOUNT_SERVICE_PATH = "/realms/{realm-name}/account";
public static final String REALM_INFO_PATH = "/realms/{realm-name}";
}
Loaded 100 of 1283 files, more files were not shown because too many files have changed in this diff. Show more