Compare commits

..
182 Commits
Author SHA1 Message Date
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
480 changed files with 52458 additions and 31267 deletions

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -2,10 +2,17 @@ body {
background-color: #F9F9F9;
}
.header .container {
position: relative;
}
.navbar-title {
background-image: url('../img/logo.png');
height: 25px;
background-repeat: no-repeat;
width: 103px;
margin: 3px 10px 5px;
text-indent: -99999px;
}
.navbar-pf {
@@ -29,13 +36,24 @@ body {
.navbar-pf .navbar-primary > .active > a,
.navbar-pf .navbar-primary > .active > a:hover{
background-color: rgba(0,0,0,0.1);
border-bottom-color: rgba(0,0,0,0.3);
border-right: rgba(0,0,0,0.3);
border-top-color: rgba(0,0,0,0.1);
font-weight: 600;
background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.1) 100%);
background-image: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.1) 100%);
background-repeat: repeat-x;
border-bottom: none;
border-right-color: rgba(255,255,255,0.15);
border-top-color: rgba(255,255,255,0.05);
}
.navbar-nav > li > .dropdown-menu {
left: 15px;
margin-top: 1px;
}
.navbar-pf .navbar-primary li.dropdown.context > a {
padding-right: 12px;
}
.navbar-pf .navbar-primary > li > a {
color: #fff !important;
}
.navbar-pf .navbar-primary li.dropdown.context > a:hover,
@@ -55,8 +73,20 @@ body {
background-repeat: repeat-x;
}
.navbar-pf .navbar-utility > li > a {
.navbar-pf .navbar-utility {
right: 15px;
top: -34px;
}
.navbar-pf .navbar-utility li.dropdown > .dropdown-toggle {
border-left: none;
padding-top: 11px;
padding-bottom: 12px;
color: #fff !important;
}
.navbar-pf .navbar-utility li.dropdown > .dropdown-toggle .pficon-user {
top: 11px;
}
.navbar-pf .navbar-utility > li > a:hover,
@@ -65,12 +95,12 @@ body {
background-color: rgba(0,0,0,0.2);
}
#content, #content-area {
background-color: #FFFFFF;
html, body, body > .container, #view {
height: 100%;
}
html, body, body > .container, #view, #content-area{
height: 100%;
body > .container {
height: calc(100% - 80px);
}
#content .glyphicon {
@@ -107,7 +137,7 @@ html, body, body > .container, #view, #content-area{
.fieldset-notice {
color: #B7B7B7;
font-size: 1.1em;
font-size: 11px;
float: right;
margin-top: -2em;
}
@@ -128,7 +158,9 @@ th.kc-table-actions {
border-color: #CECECE;
border-style: solid;
border-width: 0 1px;
padding: 0;
padding: 0 0 40px;
background-color: #fff;
min-height: 100%;
}
#content-area > * {
@@ -137,10 +169,9 @@ th.kc-table-actions {
#content-area > .nav {
border-color: #CECECE;
font-size: 1.4em;
padding: 0 20px;
margin-bottom: 0;
padding: 0.8em 0 0 2em;
padding: 10px 0 0 30px;
}
#container-right-bg {
@@ -159,15 +190,6 @@ header .navbar {
margin-bottom: 0;
}
.navbar-pf .navbar-title {
color: #ddd;
margin: .3em .4em;
padding: 0;
font-size: inherit;
font-size: 1.3em;
text-transform: uppercase;
}
#idletimeout {
background: #CC5100;
color: #FFFFFF;
@@ -193,6 +215,7 @@ header .navbar {
color: #0099D3;
letter-spacing: 0;
padding: 0;
margin-left: 0;
}
.kc-link:hover {
text-decoration: underline;
@@ -241,6 +264,10 @@ header .navbar {
font-size: 0.90909090909091em;
}
form .btn + .btn {
margin-left: 3px;
}
/* Top menu */
#kc-navigation li.active {
@@ -251,7 +278,7 @@ header .navbar {
.bs-sidebar {
background-color: #f9f9f9;
padding-top: 4.3em;
padding-top: 43px;
padding-right: 0;
padding-left: 0;
z-index: 20;
@@ -266,11 +293,10 @@ header .navbar {
margin-left: -1em;
}
.bs-sidebar ul li a {
font-size: 1.3em;
font-family: "Open Sans", sans-serif;
padding-left: 1.92307692307692em;
font-size: 13px;
padding-left: 25px;
color: #4d5258;
line-height: 2.07692307692308em;
line-height: 26px;
display: block;
border-width: 1px 0 1px 1px;
border-style: solid;
@@ -280,6 +306,7 @@ header .navbar {
.bs-sidebar ul li a:focus {
text-decoration: none;
color: #777777;
border-right: 2px solid #aaa;
}
.bs-sidebar ul li.active a {
background-color: #c7e5f0;
@@ -290,6 +317,10 @@ header .navbar {
background-position: right center;
}
.bs-sidebar ul li.active a:hover {
border-right: none;
}
/* On-Off switch */
.onoffswitch {
@@ -355,8 +386,8 @@ header .navbar {
color: #FFFFFF;
padding-left: 10px;
}
.onoffswitch.disabled .onoffswitch-inner .onoffswitch-active,
.onoffswitch.disabled .onoffswitch-inner .onoffswitch-inactive {
.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-active,
.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-inactive {
background-image: none;
background-color: #e5e5e5;
color: #9d9fa1;
@@ -377,8 +408,9 @@ header .navbar {
/* Inputs */
.form-horizontal .control-label {
text-align: left;
color: #909090;
line-height: 1.4em;
padding-top: 5px;
}
/* Details stuff */
@@ -386,9 +418,13 @@ header .navbar {
#content h2 {
font-family: "Open Sans", sans-serif;
font-weight: 100;
font-size: 2.4em;
margin-bottom: 1.04166666666667em;
margin-top: 1em;
font-size: 24px;
margin-bottom: 25px;
margin-top: 0;
}
#content h2.margin-top {
margin-top: 25px;
}
#content-area #content h2 span {
@@ -399,6 +435,14 @@ fieldset {
padding-bottom: 1em;
}
fieldset.border-top {
border-top: 1px solid #E9E8E8;
}
fieldset.border-top .form-group:first-child {
padding-top: 25px;
}
/* Legend stuff */
legend {
border-color: #E9E8E8;
@@ -406,14 +450,14 @@ legend {
border-width: 1px 0 0;
cursor: pointer;
display: block;
font-size: 1em;
font-size: 13px;
margin-bottom: 0;
padding-bottom: 1em;
padding-top: 2em;
padding-bottom: 13px;
padding-top: 18px;
}
legend .text {
font-size: 1.3em;
font-size: 13px;
font-weight: bold;
color: #505050;
}
@@ -550,7 +594,7 @@ legend .kc-icon-collapse {
background-color: rgba(0, 0, 0, 0);
border: medium none;
font-family: "Open Sans",sans-serif;
font-size: 1.1em;
font-size: 11px;
height: 30px;
line-height: 30px;
margin-left: -2px;
@@ -591,7 +635,7 @@ legend .kc-icon-collapse {
display: inline-block;
}
.changing-selectors .select-title label {
font-size: 1.1em;
font-size: 11px;
margin-left: 0.2em;
margin-bottom: 0.3em;
margin-top: 0.5em;
@@ -600,7 +644,7 @@ legend .kc-icon-collapse {
}
.changing-selectors select {
min-height: 150px;
font-size: 1.1em;
font-size: 11px;
padding: 0.545454545454545em;
min-width: 18.1818181818182em;
border: 1px #b6b6b6 solid;
@@ -665,9 +709,11 @@ legend .kc-icon-collapse {
.kc-uploaded-file {
float: left;
font-weight: bold;
margin-right: 0.909091em;
padding-top: 3px;
margin-right: 10px;
padding-top: 5px;
line-height: 1.4em;
}
.kc-link-input-file {
display: inline-block;
margin-top: 0.1em;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -21,6 +21,7 @@
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="lib/angular/angular-route.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>
@@ -66,9 +67,7 @@
</div>
<header class="navbar navbar-default navbar-pf navbar-main header">
<div class="container">
<div data-ng-include data-src="'partials/menu.html'"></div>
</div>
<div data-ng-include data-src="'partials/menu.html'"></div>
</header>
<div class="container">
@@ -67,92 +67,93 @@
</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>
<nav id="kc-navigation" class="navbar" role="navigation">
<div class="navbar-header">
<div class="container">
<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>
<div class="collapse navbar-collapse navbar-collapse-1">
<div class="container">
<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>
</div>
</nav>
</header>
<div class="container">
@@ -35,6 +35,7 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'RealmDetailCtrl'
})
*/
.when('/create/realm', {
templateUrl : 'partials/realm-create.html',
resolve : {
@@ -175,6 +176,21 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'UserRoleMappingCtrl'
})
.when('/realms/:realm/users/:user/sessions', {
templateUrl : 'partials/user-sessions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
user : function(UserLoader) {
return UserLoader();
},
stats : function(UserSessionStatsLoader) {
return UserSessionStatsLoader();
}
},
controller : 'UserSessionsCtrl'
})
.when('/realms/:realm/users', {
templateUrl : 'partials/user-list.html',
resolve : {
@@ -276,6 +292,36 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationRoleDetailCtrl'
})
.when('/realms/:realm/applications/:application/claims', {
templateUrl : 'partials/application-claims.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
},
claims : function(ApplicationClaimsLoader) {
return ApplicationClaimsLoader();
}
},
controller : 'ApplicationClaimsCtrl'
})
.when('/realms/:realm/applications/:application/sessions', {
templateUrl : 'partials/application-sessions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
},
stats : function(ApplicationSessionStatsLoader) {
return ApplicationSessionStatsLoader();
}
},
controller : 'ApplicationSessionsCtrl'
})
.when('/realms/:realm/applications/:application/credentials', {
templateUrl : 'partials/application-credentials.html',
resolve : {
@@ -303,6 +349,18 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationRoleListCtrl'
})
.when('/realms/:realm/applications/:application/revocation', {
templateUrl : 'partials/application-revocation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
}
},
controller : 'ApplicationRevocationCtrl'
})
.when('/realms/:realm/applications/:application/scope-mappings', {
templateUrl : 'partials/application-scope-mappings.html',
resolve : {
@@ -333,18 +391,6 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationInstallationCtrl'
})
.when('/realms/:realm/applications/:application/sessions', {
templateUrl : 'partials/application-sessions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
}
},
controller : 'ApplicationSessionsCtrl'
})
.when('/create/application/:realm', {
templateUrl : 'partials/application-detail.html',
resolve : {
@@ -390,6 +436,33 @@ module.config([ '$routeProvider', function($routeProvider) {
// OAUTH Client
.when('/realms/:realm/oauth-clients/:oauth/claims', {
templateUrl : 'partials/oauth-client-claims.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
claims : function(OAuthClientClaimsLoader) {
return OAuthClientClaimsLoader();
}
},
controller : 'OAuthClientClaimsCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/revocation', {
templateUrl : 'partials/oauth-client-revocation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientRevocationCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/credentials', {
templateUrl : 'partials/oauth-client-credentials.html',
resolve : {
@@ -488,6 +561,28 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmDetailCtrl'
})
.when('/realms/:realm/sessions/revocation', {
templateUrl : 'partials/session-revocation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
}
},
controller : 'RealmRevocationCtrl'
})
.when('/realms/:realm/sessions/realm', {
templateUrl : 'partials/session-realm.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
stats : function(RealmSessionStatsLoader) {
return RealmSessionStatsLoader();
}
},
controller : 'RealmSessionStatsCtrl'
})
.otherwise({
templateUrl : 'partials/notfound.html'
});
@@ -521,6 +616,8 @@ module.factory('errorInterceptor', function($q, $window, $rootScope, $location,
console.log('session timeout?');
Auth.loggedIn = false;
window.location = '/auth/rest/admin/login?path=' + $location.path();
} else if (response.status == 403) {
Notifications.error("Forbidden");
} else if (response.status == 404) {
Notifications.error("Not found");
} else if (response.status) {
@@ -608,35 +705,37 @@ module.directive('collapsed', function() {
module.directive('onoffswitch', function() {
return {
restrict: "EA",
require: 'ngModel',
replace: true,
scope: {
name: '@',
id: '@',
ngModel: '=',
ngDisabled: '=',
ngBind: '=',
name: '=',
id: '=',
onText: '@onText',
offText: '@offText'
kcOnText: '@onText',
kcOffText: '@offText'
},
// TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
//templateUrl: "templates/kc-switch.html",
template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'><label for='{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
compile: function(element, attrs) {
/*
We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
input element only to achieve proper label binding (and validity).
*/
element.removeAttr('name');
element.removeAttr('id');
if (!attrs.onText) { attrs.onText = "ON"; }
if (!attrs.offText) { attrs.offText = "OFF"; }
if (!attrs.ngDisabled) { attrs.ngDisabled = false; }
var html = "<span><div class=\"onoffswitch\" data-ng-class=\"{disabled: ngDisabled}\" tabindex=\"0\" onkeydown=\"var code = event.keyCode || event.which; if (code === 32 || code === 13) { event.stopImmediatePropagation(); event.preventDefault(); $(event.target).find('input').click();}\">" +
"<input type=\"checkbox\" data-ng-model=\"ngModel\" ng-disabled=\"ngDisabled\"" +
" class=\"onoffswitch-checkbox\" name=\"" + attrs.name + "\" id=\"" + attrs.id + "\">" +
"<label for=\"" + attrs.id + "\" class=\"onoffswitch-label\">" +
"<span class=\"onoffswitch-inner\">" +
"<span class=\"onoffswitch-active\">{{onText}}</span>" +
"<span class=\"onoffswitch-inactive\">{{offText}}</span>" +
"</span>" +
"<span class=\"onoffswitch-switch\"></span>" +
"</label>" +
"</div></span>";
element.replaceWith($(html));
element.bind('keydown', function(e){
var code = e.keyCode || e.which;
if (code === 32 || code === 13) {
e.stopImmediatePropagation();
e.preventDefault();
$(e.target).find('input').click();
}
});
}
}
});
@@ -758,7 +857,7 @@ module.directive('kcDelete', function ($compile, Notifications) {
});
module.directive('kcSelect', function ($compile, Notifications) {
module.directive('kcDropdown', function ($compile, Notifications) {
return {
scope: {
kcOptions: '=',
@@ -777,13 +876,49 @@ module.directive('kcSelect', function ($compile, Notifications) {
}
});
module.directive('kcReadOnly', function() {
var disabled = {};
var d = {
replace : false,
link : function(scope, element, attrs) {
var disable = function(i, e) {
if (!e.disabled) {
disabled[e.tagName + i] = true;
e.disabled = true;
}
}
var enable = function(i, e) {
if (disabled[e.tagName + i]) {
e.disabled = false;
delete disabled[i];
}
}
scope.$watch(attrs.kcReadOnly, function(readOnly) {
if (readOnly) {
console.debug('readonly');
element.find('input').each(disable);
element.find('button').each(disable);
element.find('select').each(disable);
element.find('textarea').each(disable);
} else {
element.find('input').each(enable);
element.find('input').each(enable);
element.find('button').each(enable);
element.find('select').each(enable);
element.find('textarea').each(enable);
}
});
}
};
return d;
});
module.directive('kcNavigation', function ($compile, Notifications) {
return {
scope: {
kcCurrent: '@',
kcRealm: '=',
kcSocial: '='
},
scope: true,
restrict: 'E',
replace: true,
templateUrl: 'templates/kc-navigation.html',
@@ -3,6 +3,10 @@ module.controller('ApplicationRoleListCtrl', function($scope, $location, realm,
$scope.roles = roles;
$scope.application = application;
for (var i = 0; i < roles.length; i++) {
console.log("role.id: " + roles[i].id + " role.name: " + roles[i].name);
}
$scope.$watch(function() {
return $location.path();
}, function() {
@@ -39,11 +43,80 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
});
});
module.controller('ApplicationSessionsCtrl', function($scope, $location, realm, application) {
module.controller('ApplicationSessionsCtrl', function($scope, realm, stats, application,
ApplicationLogoutUser,
ApplicationLogoutAll,
ApplicationSessionStats,
ApplicationSessionStatsWithUsers,
$location, Dialog, Notifications) {
$scope.realm = realm;
$scope.stats = stats;
$scope.users = {};
$scope.application = application;
$scope.toDate = function(val) {
return new Date(val);
};
$scope.loadUsers = function() {
ApplicationSessionStatsWithUsers.get({ realm : realm.realm, application: $scope.application.name }, function(updated) {
$scope.stats = updated;
$scope.users = updated.users;
})
};
$scope.logoutAll = function() {
ApplicationLogoutAll.save({realm : realm.realm, application: $scope.application.name}, function () {
Notifications.success('Logged out all users');
$scope.loadUsers();
});
};
$scope.logoutUser = function(user) {
console.log('Trying to logout user: ' + user);
ApplicationLogoutUser.save({realm : realm.realm, application: $scope.application.name, user: user}, function () {
Notifications.success('Logged out user' + user);
$scope.loadUsers();
});
};
});
module.controller('ApplicationClaimsCtrl', function($scope, realm, application, claims,
ApplicationClaims,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.application = application;
$scope.claims = angular.copy(claims);
$scope.changed = false;
$scope.$watch('claims', function () {
if (!angular.equals($scope.claims, claims)) {
$scope.changed = true;
}
}, true);
$scope.save = function () {
ApplicationClaims.update({
realm: realm.realm,
application: application.name
}, $scope.claims, function () {
$scope.changed = false;
claims = angular.copy($scope.claims);
Notifications.success("Your claim changes have been saved.");
});
};
$scope.reset = function () {
$location.url("/realms/" + realm.realm + "/applications/" + application.name + "/claims");
};
});
module.controller('ApplicationRoleDetailCtrl', function($scope, realm, application, role, roles, applications,
Role, ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
$http, $location, Dialog, Notifications) {
@@ -144,16 +217,43 @@ module.controller('ApplicationInstallationCtrl', function($scope, realm, applica
module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {
console.log('ApplicationDetailCtrl');
$scope.clientTypes = [
"confidential",
"public",
"bearer-only"
];
$scope.realm = realm;
$scope.create = !application.name;
if (!$scope.create) {
$scope.application= angular.copy(application);
$scope.clientType = $scope.clientTypes[0];
if (application.bearerOnly) {
$scope.clientType = $scope.clientTypes[2];
} else if (application.publicClient) {
$scope.clientType = $scope.clientTypes[1];
}
} else {
$scope.application = {};
$scope.application = { enabled: true };
$scope.application.webOrigins = [];
$scope.application.redirectUris = [];
$scope.clientType = $scope.clientTypes[0];
}
$scope.changeClientType = function() {
console.log('Client Type: ' + $scope.clientType);
if ($scope.clientType == "confidential") {
$scope.application.bearerOnly = false;
$scope.application.publicClient = false;
} else if ($scope.clientType == "public") {
$scope.application.bearerOnly = false;
$scope.application.publicClient = true;
} else if ($scope.clientType == "bearer-only") {
$scope.application.bearerOnly = true;
$scope.application.publicClient = false;
}
};
$scope.$watch(function() {
return $location.path();
}, function() {
@@ -350,3 +450,49 @@ module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm,
});
module.controller('ApplicationRevocationCtrl', function($scope, realm, application, Application, ApplicationPushRevocation, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.application = application;
var setNotBefore = function() {
if ($scope.application.notBefore == 0) {
$scope.notBefore = "None";
} else {
$scope.notBefore = new Date($scope.application.notBefore * 1000);
}
};
setNotBefore();
var refresh = function() {
Application.get({ realm : realm.realm, application: $scope.application.name }, function(updated) {
$scope.application = updated;
setNotBefore();
})
};
$scope.clear = function() {
$scope.application.notBefore = 0;
Application.update({ realm : realm.realm, application: application.name}, $scope.application, function () {
$scope.notBefore = "None";
Notifications.success('Not Before cleared for application.');
refresh();
});
}
$scope.setNotBeforeNow = function() {
$scope.application.notBefore = new Date().getTime()/1000;
Application.update({ realm : realm.realm, application: $scope.application.name}, $scope.application, function () {
Notifications.success('Not Before cleared for application.');
refresh();
});
}
$scope.pushRevocation = function() {
ApplicationPushRevocation.save({realm : realm.realm, application: $scope.application.name}, function () {
Notifications.success('Push sent for application.');
});
}
});
@@ -1,3 +1,37 @@
module.controller('OAuthClientClaimsCtrl', function($scope, realm, oauth, claims,
OAuthClientClaims,
$location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
$scope.claims = angular.copy(claims);
$scope.changed = false;
$scope.$watch('claims', function () {
if (!angular.equals($scope.claims, claims)) {
$scope.changed = true;
}
}, true);
$scope.save = function () {
OAuthClientClaims.update({
realm: realm.realm,
oauth: oauth.id
}, $scope.claims, function () {
$scope.changed = false;
claims = angular.copy($scope.claims);
Notifications.success("Your claim changes have been saved.");
});
};
$scope.reset = function () {
$location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id + "/claims");
};
});
module.controller('OAuthClientCredentialsCtrl', function($scope, $location, realm, oauth, OAuthClientCredentials, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
@@ -42,12 +76,33 @@ module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, O
module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.create = !oauth.id;
$scope.clientTypes = [
"confidential",
"public"
];
$scope.changeClientType = function() {
console.log('Client Type: ' + $scope.clientType);
if ($scope.clientType == "confidential") {
$scope.oauth.publicClient = false;
} else if ($scope.clientType == "public") {
$scope.oauth.publicClient = true;
}
};
if (!$scope.create) {
$scope.oauth= angular.copy(oauth);
$scope.clientType = $scope.clientTypes[0];
if (oauth.publicClient) {
$scope.clientType = $scope.clientTypes[1];
}
} else {
$scope.oauth = {};
$scope.oauth = { enabled: true };
$scope.oauth.webOrigins = [];
$scope.oauth.redirectUris = [];
$scope.clientType = $scope.clientTypes[0];
}
$scope.$watch(function() {
@@ -253,3 +308,43 @@ module.controller('OAuthClientInstallationCtrl', function($scope, realm, install
$scope.installation = installation;
$scope.download = OAuthClientInstallation.url({ realm: $routeParams.realm, oauth: $routeParams.oauth });
});
module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
$scope.oauth = oauth;
$scope.realm = realm;
var setNotBefore = function() {
if ($scope.oauth.notBefore == 0) {
$scope.notBefore = "None";
} else {
$scope.notBefore = new Date($scope.oauth.notBefore * 1000);
}
};
setNotBefore();
var refresh = function() {
OAuthClient.get({ realm : realm.realm, id: $scope.oauth.id }, function(updated) {
$scope.oauth = updated;
setNotBefore();
})
};
$scope.clear = function() {
$scope.oauth.notBefore = 0;
OAuthClient.update({ realm : realm.realm, id: $scope.oauth.id}, $scope.oauth, function () {
$scope.notBefore = "None";
Notifications.success('Not Before cleared for application.');
refresh();
});
}
$scope.setNotBeforeNow = function() {
$scope.oauth.notBefore = new Date().getTime()/1000;
OAuthClient.update({ realm : realm.realm, id: $scope.oauth.id}, $scope.oauth, function () {
Notifications.success('Not Before cleared for application.');
refresh();
});
}
});
@@ -7,6 +7,57 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
$http.get('/auth/rest/admin/whoami').success(function(data, status) {
Auth.user = data;
Auth.loggedIn = true;
function getAccess(role) {
if (!Current.realm) {
return false;
}
var realmAccess = Auth.user['realm_access'];
if (realmAccess) {
realmAccess = realmAccess[Current.realm.realm];
if (realmAccess) {
return realmAccess.indexOf(role) >= 0;
}
}
return false;
}
$scope.access = {
createRealm: data.createRealm,
get viewRealm() {
return getAccess('view-realm') || this.manageRealm;
},
get viewApplications() {
return getAccess('view-applications') || this.manageApplications;
},
get viewClients() {
return getAccess('view-clients') || this.manageClients;
},
get viewUsers() {
return getAccess('view-users') || this.manageClients;
},
get manageRealm() {
return getAccess('manage-realm');
},
get manageApplications() {
return getAccess('manage-applications');
},
get manageClients() {
return getAccess('manage-clients');
},
get manageUsers() {
return getAccess('manage-users');
}
}
})
.error(function(data, status) {
Auth.loggedIn = false;
@@ -20,15 +71,15 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
});
});
module.controller('HomeCtrl', function(Realm, $location) {
module.controller('HomeCtrl', function(Realm, Auth, $location) {
Realm.query(null, function(realms) {
var realm;
if (realms.length == 1) {
realm = realms[0].realm;
} else if (realms.length == 2) {
if (realms[0].realm == 'keycloak-admin') {
if (realms[0].realm == Auth.user.realm) {
realm = realms[1].realm;
} else if (realms[1].realm == 'administration') {
} else if (realms[1].realm == Auth.user.realm) {
realm = realms[0].realm;
}
}
@@ -62,7 +113,7 @@ module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $l
}
});
module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, $location, Dialog, Notifications) {
module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, $location, Dialog, Notifications, Auth) {
console.log('RealmCreateCtrl');
Current.realm = null;
@@ -100,9 +151,23 @@ module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $
//formDataAppender: function(formData, key, val){}
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
Notifications.success("The realm has been uploaded.");
$location.url("/realms");
}).success(function(data, status, headers) {
Realm.query(function(data) {
Current.realms = data;
$http.get('/auth/rest/admin/whoami').success(function(user) {
Auth.user = user;
Notifications.success("The realm has been uploaded.");
var location = headers('Location');
if (location) {
$location.url("/realms/" + location.substring(location.lastIndexOf('/') + 1));
} else {
$location.url("/realms");
}
});
});
})
.error(function() {
Notifications.error("The realm can not be uploaded. Please verify the file.");
@@ -123,16 +188,16 @@ module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $
var ssl = window.location.protocol == 'https:';
realmCopy.sslNotRequired = !ssl;
console.log('creating new realm **');
Realm.create(realmCopy, function(data, headers) {
var data = Realm.query(function() {
Realm.create(realmCopy, function() {
Realm.query(function(data) {
Current.realms = data;
for (var i = 0; i < Current.realms.length; i++) {
if (Current.realms[i].realm == realmCopy.realm) {
Current.realm = Current.realms[i];
}
}
$location.url("/realms/" + realmCopy.realm);
Notifications.success("The realm has been created.");
$http.get('/auth/rest/admin/whoami').success(function(user) {
Auth.user = user;
$location.url("/realms/" + realmCopy.realm);
Notifications.success("The realm has been created.");
});
});
});
};
@@ -445,7 +510,7 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
// Update/save the selected application with new default roles.
Application.update({
realm: $scope.realm.realm,
id: $scope.application.name
application: $scope.application.name
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
});
@@ -469,7 +534,7 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
// Update/save the selected application with new default roles.
Application.update({
realm: $scope.realm.realm,
id: $scope.application.name
application: $scope.application.name
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
});
@@ -543,10 +608,22 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
$scope.realm = realm;
$scope.realm.tokenLifespanUnit = TimeUnit.autoUnit(realm.tokenLifespan);
$scope.realm.tokenLifespan = TimeUnit.toUnit(realm.tokenLifespan, $scope.realm.tokenLifespanUnit);
$scope.$watch('realm.tokenLifespanUnit', function(to, from) {
$scope.realm.tokenLifespan = TimeUnit.convert($scope.realm.tokenLifespan, from, to);
$scope.realm.accessTokenLifespanUnit = TimeUnit.autoUnit(realm.accessTokenLifespan);
$scope.realm.accessTokenLifespan = TimeUnit.toUnit(realm.accessTokenLifespan, $scope.realm.accessTokenLifespanUnit);
$scope.$watch('realm.accessTokenLifespanUnit', function(to, from) {
$scope.realm.accessTokenLifespan = TimeUnit.convert($scope.realm.accessTokenLifespan, from, to);
});
$scope.realm.centralLoginLifespanUnit = TimeUnit.autoUnit(realm.centralLoginLifespan);
$scope.realm.centralLoginLifespan = TimeUnit.toUnit(realm.centralLoginLifespan, $scope.realm.centralLoginLifespanUnit);
$scope.$watch('realm.centralLoginLifespanUnit', function(to, from) {
$scope.realm.centralLoginLifespan = TimeUnit.convert($scope.realm.centralLoginLifespan, from, to);
});
$scope.realm.refreshTokenLifespanUnit = TimeUnit.autoUnit(realm.refreshTokenLifespan);
$scope.realm.refreshTokenLifespan = TimeUnit.toUnit(realm.refreshTokenLifespan, $scope.realm.refreshTokenLifespanUnit);
$scope.$watch('realm.refreshTokenLifespanUnit', function(to, from) {
$scope.realm.refreshTokenLifespan = TimeUnit.convert($scope.realm.refreshTokenLifespan, from, to);
});
$scope.realm.accessCodeLifespanUnit = TimeUnit.autoUnit(realm.accessCodeLifespan);
@@ -573,11 +650,15 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
$scope.save = function() {
var realmCopy = angular.copy($scope.realm);
delete realmCopy["tokenLifespanUnit"];
delete realmCopy["accessTokenLifespanUnit"];
delete realmCopy["refreshTokenLifespanUnit"];
delete realmCopy["accessCodeLifespanUnit"];
delete realmCopy["centralLoginLifespanUnit"];
delete realmCopy["accessCodeLifespanUserActionUnit"];
realmCopy.tokenLifespan = TimeUnit.toSeconds($scope.realm.tokenLifespan, $scope.realm.tokenLifespanUnit)
realmCopy.accessTokenLifespan = TimeUnit.toSeconds($scope.realm.accessTokenLifespan, $scope.realm.accessTokenLifespanUnit)
realmCopy.centralLoginLifespan = TimeUnit.toSeconds($scope.realm.centralLoginLifespan, $scope.realm.centralLoginLifespanUnit)
realmCopy.refreshTokenLifespan = TimeUnit.toSeconds($scope.realm.refreshTokenLifespan, $scope.realm.refreshTokenLifespanUnit)
realmCopy.accessCodeLifespan = TimeUnit.toSeconds($scope.realm.accessCodeLifespan, $scope.realm.accessCodeLifespanUnit)
realmCopy.accessCodeLifespanUserAction = TimeUnit.toSeconds($scope.realm.accessCodeLifespanUserAction, $scope.realm.accessCodeLifespanUserActionUnit)
@@ -609,6 +690,69 @@ module.controller('RealmKeysDetailCtrl', function($scope, Realm, realm, $http, $
};
});
module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmSessionStats, RealmLogoutAll, Notifications) {
$scope.realm = realm;
$scope.stats = stats;
console.log(stats);
$scope.logoutAll = function() {
RealmLogoutAll.save({realm : realm.realm}, function () {
Notifications.success('Logged out all users');
RealmSessionStats.get({realm: realm.realm}, function(updated) {
Notifications.success('Logged out all users');
$scope.stats = updated;
})
});
};
});
module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevocation, realm, $http, $location, Dialog, Notifications) {
$scope.realm = angular.copy(realm);
var setNotBefore = function() {
if ($scope.realm.notBefore == 0) {
$scope.notBefore = "None";
} else {
$scope.notBefore = new Date($scope.realm.notBefore * 1000);
}
};
setNotBefore();
var reset = function() {
Realm.get({ id : realm.realm }, function(updated) {
$scope.realm = updated;
setNotBefore();
})
};
$scope.clear = function() {
Realm.update({ realm: realm.realm, notBefore : 0 }, function () {
$scope.notBefore = "None";
Notifications.success('Not Before cleared for realm.');
reset();
});
}
$scope.setNotBeforeNow = function() {
Realm.update({ realm: realm.realm, notBefore : new Date().getTime()/1000}, function () {
Notifications.success('Not Before cleared for realm.');
reset();
});
}
$scope.pushRevocation = function() {
RealmPushRevocation.save({ realm: realm.realm}, function () {
Notifications.success('Push sent for realm.');
});
}
});
module.controller('RoleListCtrl', function($scope, $location, realm, roles) {
$scope.realm = realm;
@@ -118,6 +118,35 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ro
});
module.controller('UserSessionsCtrl', function($scope, realm, user, stats, UserLogout, ApplicationLogoutUser, UserSessionStats, Notifications) {
$scope.realm = realm;
$scope.user = user;
$scope.stats = stats;
$scope.logoutAll = function() {
UserLogout.save({realm : realm.realm, user: user.username}, function () {
Notifications.success('Logged out user in all applications');
UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
$scope.stats = updated;
})
});
};
$scope.logoutApplication = function(app) {
console.log('log user out of app: ' + app);
ApplicationLogoutUser.save({realm : realm.realm, application: app, user: user.username}, function () {
Notifications.success('Logged out user from application');
UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
$scope.stats = updated;
})
});
};
});
module.controller('UserListCtrl', function($scope, realm, User) {
$scope.realm = realm;
$scope.searchQuery = function() {
@@ -143,41 +172,20 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
$scope.user = angular.copy(user);
$scope.create = !user.username;
if ($scope.create) {
$scope.user.enabled = true;
}
$scope.changed = false; // $scope.create;
// ID - Name map for required actions. IDs are enum names.
var userReqActionList = [
$scope.userReqActionList = [
{id: "VERIFY_EMAIL", text: "Verify Email"},
{id: "UPDATE_PROFILE", text: "Update Profile"},
{id: "CONFIGURE_TOTP", text: "Configure Totp"},
{id: "UPDATE_PASSWORD", text: "Update Password"}
];
// Options for the req actions tag selector
$scope.userReqActionsOptions = {
'multiple' : true,
'tags' : userReqActionList
};
// Model for the req actions tag selector
$scope.userActions = [];
for (var i = 0; i < userReqActionList.length; i++){
var action = userReqActionList[i];
if ($scope.user.requiredActions && $scope.user.requiredActions.indexOf(action.id) > -1){
$scope.userActions.push({id: action.id, text: action.text});
}
}
// Watching ui-select2 model to properly format the req actions for user
$scope.$watch("userActions", function(newValue, oldValue) {
$scope.user.requiredActions = [];
for (var i=0; i < newValue.length; i++){
var action = newValue[i];
$scope.user.requiredActions.push(action.id);
}
});
$scope.$watch('user', function() {
if (!angular.equals($scope.user, user)) {
$scope.changed = true;
@@ -48,20 +48,37 @@ module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
});
module.factory('UserListLoader', function(Loader, User, $route, $q) {
return Loader.query(User, function() {
return {
realm : $route.current.params.realm
}
});
return Loader.query(User, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $route, $q) {
return Loader.get(RealmSessionStats, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('UserLoader', function(Loader, User, $route, $q) {
return Loader.get(User, function() {
return {
realm : $route.current.params.realm,
userId : $route.current.params.user
}
});
return Loader.get(User, function() {
return {
realm : $route.current.params.realm,
userId : $route.current.params.user
}
});
});
module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
return Loader.get(UserSessionStats, function() {
return {
realm : $route.current.params.realm,
user : $route.current.params.user
}
});
});
module.factory('RoleLoader', function(Loader, Role, $route, $q) {
@@ -91,6 +108,24 @@ module.factory('ApplicationRoleLoader', function(Loader, ApplicationRole, $route
});
});
module.factory('ApplicationSessionStatsLoader', function(Loader, ApplicationSessionStats, $route, $q) {
return Loader.get(ApplicationSessionStats, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
}
});
});
module.factory('ApplicationClaimsLoader', function(Loader, ApplicationClaims, $route, $q) {
return Loader.get(ApplicationClaims, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
}
});
});
module.factory('ApplicationInstallationLoader', function(Loader, ApplicationInstallation, $route, $q) {
return Loader.get(ApplicationInstallation, function() {
return {
@@ -149,6 +184,16 @@ module.factory('OAuthClientLoader', function(Loader, OAuthClient, $route, $q) {
});
});
module.factory('OAuthClientClaimsLoader', function(Loader, OAuthClientClaims, $route, $q) {
return Loader.get(OAuthClientClaims, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth
}
});
});
module.factory('OAuthClientListLoader', function(Loader, OAuthClient, $route, $q) {
return Loader.query(OAuthClient, function() {
return {
@@ -1,6 +1,6 @@
'use strict';
var module = angular.module('keycloak.services', [ 'ngResource' ]);
var module = angular.module('keycloak.services', [ 'ngResource', 'ngRoute' ]);
module.service('Dialog', function($dialog) {
var dialog = {};
@@ -147,14 +147,27 @@ module.factory('ServerInfo', function($resource) {
});
module.factory('User', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
realm : '@realm',
userId : '@userId'
}, {
return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
realm : '@realm',
userId : '@userId'
}, {
update : {
method : 'PUT'
}
});
});
});
module.factory('UserSessionStats', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/users/:user/session-stats', {
realm : '@realm',
user : '@user'
});
});
module.factory('UserLogout', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/users/:user/logout', {
realm : '@realm',
user : '@user'
});
});
module.factory('UserCredentials', function($resource) {
@@ -235,6 +248,19 @@ module.factory('RoleRealmComposites', function($resource) {
});
});
module.factory('RealmPushRevocation', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/push-revocation', {
realm : '@realm'
});
});
module.factory('RealmSessionStats', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/session-stats', {
realm : '@realm'
});
});
module.factory('RoleApplicationComposites', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/applications/:application', {
realm : '@realm',
@@ -451,6 +477,58 @@ module.factory('ApplicationRole', function($resource) {
});
});
module.factory('ApplicationClaims', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/claims', {
realm : '@realm',
application : "@application"
}, {
update : {
method : 'PUT'
}
});
});
module.factory('ApplicationSessionStats', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats', {
realm : '@realm',
application : "@application"
});
});
module.factory('ApplicationSessionStatsWithUsers', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats?users=true', {
realm : '@realm',
application : "@application"
});
});
module.factory('ApplicationLogoutAll', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-all', {
realm : '@realm',
application : "@application"
});
});
module.factory('ApplicationLogoutUser', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-user/:user', {
realm : '@realm',
application : "@application",
user : "@user"
});
});
module.factory('RealmLogoutAll', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/logout-all', {
realm : '@realm'
});
});
module.factory('ApplicationPushRevocation', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/push-revocation', {
realm : '@realm',
application : "@application"
});
});
module.factory('Application', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application', {
@@ -516,6 +594,18 @@ module.factory('OAuthClient', function($resource) {
});
});
module.factory('OAuthClientClaims', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/claims', {
realm : '@realm',
oauth : "@oauth"
}, {
update : {
method : 'PUT'
}
});
});
module.factory('OAuthClientCredentials', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/client-secret', {
realm : '@realm',
+22 -5
View File
@@ -1,14 +1,25 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* @license AngularJS v1.2.13
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @name ngCookies
* @description
*
* # ngCookies
*
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
*
* {@installModule cookies}
*
* <div doc-module-components="ngCookies"></div>
*
* See {@link ngCookies.$cookies `$cookies`} and
* {@link ngCookies.$cookieStore `$cookieStore`} for usage.
*/
@@ -24,6 +35,8 @@ angular.module('ngCookies', ['ng']).
* Only a simple Object is exposed and by adding or removing properties to/from
* this object, new cookies are created/deleted at the end of current $eval.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* @example
<doc:example>
<doc:source>
@@ -68,7 +81,8 @@ angular.module('ngCookies', ['ng']).
/**
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
* Pushes all the cookies from the service to the browser and verifies if all cookies were
* stored.
*/
function push() {
var name,
@@ -128,6 +142,9 @@ angular.module('ngCookies', ['ng']).
* Provides a key-value (string-object) storage, that is backed by session cookies.
* Objects put or retrieved from this storage are automatically serialized or
* deserialized by angular's toJson/fromJson.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* @example
*/
factory('$cookieStore', ['$cookies', function($cookies) {
+153 -20
View File
@@ -1,10 +1,84 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* @license AngularJS v1.2.13
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(
(function() {'use strict';
/**
* @description
*
* This object provides a utility for producing rich Error messages within
* Angular. It can be called as follows:
*
* var exampleMinErr = minErr('example');
* throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
*
* The above creates an instance of minErr in the example namespace. The
* resulting error will have a namespaced error code of example.one. The
* resulting error will replace {0} with the value of foo, and {1} with the
* value of bar. The object is not restricted in the number of arguments it can
* take.
*
* If fewer arguments are specified than necessary for interpolation, the extra
* interpolation markers will be preserved in the final string.
*
* Since data will be parsed statically during a build step, some restrictions
* are applied with respect to how minErr instances are created and called.
* Instances should have names of the form namespaceMinErr for a minErr created
* using minErr('namespace') . Error codes, namespaces and template strings
* should all be static strings, not variables or general expressions.
*
* @param {string} module The namespace to use for the new minErr instance.
* @returns {function(string, string, ...): Error} instance
*/
function minErr(module) {
return function () {
var code = arguments[0],
prefix = '[' + (module ? module + ':' : '') + code + '] ',
template = arguments[1],
templateArgs = arguments,
stringify = function (obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (typeof obj === 'undefined') {
return 'undefined';
} else if (typeof obj !== 'string') {
return JSON.stringify(obj);
}
return obj;
},
message, i;
message = prefix + template.replace(/\{\d+\}/g, function (match) {
var index = +match.slice(1, -1), arg;
if (index + 2 < templateArgs.length) {
arg = templateArgs[index + 2];
if (typeof arg === 'function') {
return arg.toString().replace(/ ?\{[\s\S]*$/, '');
} else if (typeof arg === 'undefined') {
return 'undefined';
} else if (typeof arg !== 'string') {
return toJson(arg);
}
return arg;
}
return match;
});
message = message + '\nhttp://errors.angularjs.org/1.2.13/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
encodeURIComponent(stringify(arguments[i]));
}
return new Error(message);
};
}
/**
* @ngdoc interface
@@ -16,11 +90,19 @@
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
return ensure(ensure(window, 'angular', Object), 'module', function() {
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** @type {Object.<string, angular.Module>} */
var modules = {};
@@ -29,15 +111,19 @@ function setupModuleLoader(window) {
* @name angular.module
* @description
*
* The `angular.module` is a global place for creating and registering Angular modules. All
* modules (angular core or 3rd party) that should be available to an application must be
* The `angular.module` is a global place for creating, registering and retrieving Angular
* modules.
* All modules (angular core or 3rd party) that should be available to an application must be
* registered using this mechanism.
*
* When passed two or more arguments, a new module is created. If passed only one argument, an
* existing module (the name passed as the first argument to `module`) is retrieved.
*
*
* # Module
*
* A module is a collocation of services, directives, filters, and configuration information. Module
* is used to configure the {@link AUTO.$injector $injector}.
* A module is a collection of services, directives, filters, and configuration information.
* `angular.module` is used to configure the {@link AUTO.$injector $injector}.
*
* <pre>
* // Create a new module
@@ -48,7 +134,6 @@ function setupModuleLoader(window) {
*
* // configure existing services inside initialization blocks.
* myModule.config(function($locationProvider) {
'use strict';
* // Configure existing providers
* $locationProvider.hashPrefix('!');
* });
@@ -65,19 +150,28 @@ function setupModuleLoader(window) {
* {@link angular.bootstrap} to simplify this process for you.
*
* @param {!string} name The name of the module to create or retrieve.
* @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
* the module is being retrieved for further configuration.
* @param {Array.<string>=} requires If specified then new module is being created. If
* unspecified then the the module is being retrieved for further configuration.
* @param {Function} configFn Optional configuration function for the module. Same as
* {@link angular.Module#config Module#config()}.
* {@link angular.Module#methods_config Module#config()}.
* @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw Error('No module: ' + name);
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** @type {!Array.<Array.<*>>} */
@@ -100,7 +194,8 @@ function setupModuleLoader(window) {
* @propertyOf angular.Module
* @returns {Array.<string>} List of module names which must be loaded before this module.
* @description
* Holds the list of modules which the injector will load before the current module is loaded.
* Holds the list of modules which the injector will load before the current module is
* loaded.
*/
requires: requires,
@@ -119,7 +214,8 @@ function setupModuleLoader(window) {
* @name angular.Module#provider
* @methodOf angular.Module
* @param {string} name service name
* @param {Function} providerType Construction function for creating new instance of the service.
* @param {Function} providerType Construction function for creating new instance of the
* service.
* @description
* See {@link AUTO.$provide#provider $provide.provider()}.
*/
@@ -170,6 +266,40 @@ function setupModuleLoader(window) {
*/
constant: invokeLater('$provide', 'constant', 'unshift'),
/**
* @ngdoc method
* @name angular.Module#animation
* @methodOf angular.Module
* @param {string} name animation name
* @param {Function} animationFactory Factory function for creating new instance of an
* animation.
* @description
*
* **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
*
*
* Defines an animation hook that can be later used with
* {@link ngAnimate.$animate $animate} service and directives that use this service.
*
* <pre>
* module.animation('.animation-name', function($inject1, $inject2) {
* return {
* eventName : function(element, done) {
* //code to run the animation
* //once complete, then run done()
* return function cancellationFunction(element) {
* //code to cancel the animation
* }
* }
* }
* })
* </pre>
*
* See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information.
*/
animation: invokeLater('$animateProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#filter
@@ -185,7 +315,8 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#controller
* @methodOf angular.Module
* @param {string} name Controller name.
* @param {string|Object} name Controller name, or an object map of controllers where the
* keys are the names and the values are the constructors.
* @param {Function} constructor Controller constructor function.
* @description
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
@@ -196,11 +327,12 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#directive
* @methodOf angular.Module
* @param {string} name directive name
* @param {string|Object} name Directive name, or an object map of directives where the
* keys are the names and the values are the factories.
* @param {Function} directiveFactory Factory function for creating new instance of
* directives.
* @description
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
* See {@link ng.$compileProvider#methods_directive $compileProvider.directive()}.
*/
directive: invokeLater('$compileProvider', 'directive'),
@@ -247,7 +379,7 @@ function setupModuleLoader(window) {
return function() {
invokeQueue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
}
};
}
});
};
@@ -255,7 +387,8 @@ function setupModuleLoader(window) {
}
)(window);
setupModuleLoader(window);
})(window);
/**
* Closure compiler type information
File diff suppressed because it is too large. Load diff
+302 -163
View File
@@ -1,15 +1,68 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* @license AngularJS v1.2.13
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
(function(window, angular, undefined) {'use strict';
var $resourceMinErr = angular.$$minErr('$resource');
// Helper functions and regex to lookup a dotted path on an object
// stopping at undefined/null. The path must be composed of ASCII
// identifiers (just like $parse)
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
function isValidDottedPath(path) {
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
MEMBER_NAME_REGEX.test('.' + path));
}
function lookupDottedPath(obj, path) {
if (!isValidDottedPath(path)) {
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
}
var keys = path.split('.');
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
var key = keys[i];
obj = (obj !== null) ? obj[key] : undefined;
}
return obj;
}
/**
* Create a shallow copy of an object and clear other fields from the destination
*/
function shallowClearAndCopy(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key){
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc overview
* @name ngResource
* @description
*
* # ngResource
*
* The `ngResource` module provides interaction support with RESTful services
* via the $resource service.
*
* {@installModule resource}
*
* <div doc-module-components="ngResource"></div>
*
* See {@link ngResource.$resource `$resource`} for usage.
*/
/**
@@ -24,24 +77,22 @@
* 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}.
* Requires the {@link ngResource `ngResource`} module to be installed.
*
* Finally load the module in your application:
* @param {string} url A parametrized 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`), it will be respected.
*
* 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')`.
* If you are using a url with a suffix, just add the suffix, like this:
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
* or even `$resource('http://example.com/resource/:resource_id.:format')`
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
* can escape it with `/\.`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods.
* `actions` methods. If any of the parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overridden).
*
* 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 `?`.
@@ -53,21 +104,48 @@
* 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:
* default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage_parameters $http.config}:
*
* {action1: {method:?, params:?, isArray:?},
* action2: {method:?, params:?, isArray:?},
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...},
* ...}
*
* 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.
* - **`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. If any of
* the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden).
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
* like for the resource-level urls.
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
* see `returns` section.
* - **`transformRequest`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* - **`transformResponse`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
* caching.
* - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
* should abort the request when resolved.
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
* requests with credentials} for more information.
* - **`responseType`** - `{string}` - see {@link
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
*
* @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:
@@ -97,7 +175,7 @@
* 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.
* means that in most cases 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:
@@ -106,6 +184,28 @@
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
* with (httpResponse) argument.
*
* Class actions return empty instance (with additional properties below).
* Instance actions return promise of the action.
*
* The Resource instances and collection have these additional properties:
*
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
* instance or collection.
*
* On success, the promise is resolved with the same resource instance or collection object,
* updated with data from server. This makes it easy to use in
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
* rendering until the resource(s) are loaded.
*
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
* the `resource` property.
*
* - `$resolved`: `true` after first server interaction is completed (either with success or
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
* data-binding.
*
* @example
*
@@ -142,14 +242,15 @@
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
// server returns: {id:789, number:'0123', 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`.
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
* `headers`.
* 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.
@@ -162,7 +263,7 @@
});
</pre>
*
* It's worth noting that the success callback for `get`, `query` and other method gets passed
* It's worth noting that the success callback for `get`, `query` and other methods 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:
*
@@ -177,56 +278,38 @@
});
</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>
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* <pre>
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
* 'update': { method:'PUT' }
* });
* }]);
*
* // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
* // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id });
* $id = note.id;
*
* // Now call update passing in the ID first then the object you are updating
* Notes.update({ id:$id }, note);
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* </pre>
*/
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$parse', function($http, $parse) {
factory('$resource', ['$http', '$q', function($http, $q) {
var DEFAULT_ACTIONS = {
'get': {method:'GET'},
'save': {method:'POST'},
@@ -238,10 +321,7 @@ angular.module('ngResource', ['ng']).
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
isFunction = angular.isFunction,
getter = function(obj, path) {
return $parse(path)(obj);
};
isFunction = angular.isFunction;
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
@@ -263,9 +343,9 @@ angular.module('ngResource', ['ng']).
/**
* 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:
* This method is intended for encoding *key* or *value* parts of query component. We need a
* custom method because encodeURIComponent is too aggressive 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 / "-" / "." / "_" / "~"
@@ -283,32 +363,40 @@ angular.module('ngResource', ['ng']).
}
function Route(template, defaults) {
this.template = template = template + '#';
this.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, ':');
this.urlParams = {};
}
Route.prototype = {
url: function(params) {
setUrlParams: function(config, params, actionUrl) {
var self = this,
url = this.template,
url = actionUrl || self.template,
val,
encodedVal;
var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param){
if (param === 'hasOwnProperty') {
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
}
if (!(new RegExp("^\\d+$").test(param)) && param &&
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
urlParams[param] = true;
}
});
url = url.replace(/\\:/g, ':');
params = params || {};
forEach(this.urlParams, function(_, urlParam){
forEach(self.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");
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W)", "g"), function(match,
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
@@ -318,21 +406,28 @@ angular.module('ngResource', ['ng']).
});
}
});
url = url.replace(/\/?#$/, '');
var query = [];
// strip trailing slashes and set the url
url = url.replace(/\/+$/, '') || '/';
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key){
if (!self.urlParams[key]) {
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
config.params = config.params || {};
config.params[key] = value;
}
});
query.sort();
url = url.replace(/\/*$/, '');
return url + (query.length ? '?' + query.join('&') : '');
}
};
function ResourceFactory(url, paramDefaults, actions) {
function resourceFactory(url, paramDefaults, actions) {
var route = new Route(url);
actions = extend({}, DEFAULT_ACTIONS, actions);
@@ -341,23 +436,28 @@ angular.module('ngResource', ['ng']).
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key){
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value){
copy(value || {}, this);
shallowClearAndCopy(value || {}, this);
}
forEach(actions, function(action, name) {
action.method = angular.uppercase(action.method);
var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {};
var data;
var success = noop;
var error = null;
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch(arguments.length) {
case 4:
error = a4;
@@ -388,69 +488,108 @@ angular.module('ngResource', ['ng']).
break;
case 0: break;
default:
throw "Expected between 0-4 arguments [params, data, success, error], got " +
arguments.length + " arguments.";
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
var httpConfig = {};
var responseInterceptor = action.interceptor && action.interceptor.response ||
defaultResponseInterceptor;
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
undefined;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
httpConfig[key] = copy(value);
}
});
if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
extend({}, extractParams(data, action.params || {}), params),
action.url);
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
'response to contain an {0} but got an {1}',
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.$resolved = true;
(error||noop)(response);
return $q.reject(response);
});
promise = promise.then(
function(response) {
var value = responseInterceptor(response);
(success||noop)(value, response.headers);
return value;
},
responseErrorInterceptor);
if (!isInstanceCall) {
// we are creating instance / collection
// - set the initial promise
// - return the instance / collection
value.$promise = promise;
value.$resolved = false;
return value;
}
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;
// instance call
return promise;
};
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.";
Resource.prototype['$' + name] = function(params, success, error) {
if (isFunction(params)) {
error = success; success = params; params = {};
}
var data = hasBody ? this : undefined;
Resource[name].call(this, params, data, success, error);
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults){
return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return ResourceFactory;
return resourceFactory;
}]);
@@ -0,0 +1,921 @@
/**
* @license AngularJS v1.2.13
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @name ngRoute
* @description
*
* # ngRoute
*
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* {@installModule route}
*
* <div doc-module-components="ngRoute"></div>
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider);
/**
* @ngdoc object
* @name ngRoute.$routeProvider
* @function
*
* @description
*
* Used for configuring routes.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function $RouteProvider(){
function inherit(parent, extra) {
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
}
var routes = {};
/**
* @ngdoc method
* @name ngRoute.$routeProvider#when
* @methodOf ngRoute.$routeProvider
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashs/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashs`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*
* Object properties:
*
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
* published to scope under the `controllerAs` name.
* - `template` – `{string=|function()=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, the router
* will wait for them all to be resolved or one to be rejected before the controller is
* instantiated.
* If all the promises are resolved successfully, the values of the resolved promises are
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
* fired. If any of the promises are rejected the
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` – {(string|function())=} – value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.<string>}` - route parameters extracted from the current
* `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.path()` and `$location.search()`.
*
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
* or `$location.hash()` changes.
*
* If the option is set to `false` and url in the browser changes, then
* `$routeUpdate` event is broadcasted on the root scope.
*
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
*
* If the option is set to `true`, then the particular route can be matched without being
* case sensitive
*
* @returns {Object} self
*
* @description
* Adds a new route definition to the `$route` service.
*/
this.when = function(path, route) {
routes[path] = angular.extend(
{reloadOnSearch: true},
route,
path && pathRegExp(path, route)
);
// create redirection for trailing slashes
if (path) {
var redirectPath = (path[path.length-1] == '/')
? path.substr(0, path.length-1)
: path +'/';
routes[redirectPath] = angular.extend(
{redirectTo: path},
pathRegExp(redirectPath, route)
);
}
return this;
};
/**
* @param path {string} path
* @param opts {Object} options
* @return {?Object}
*
* @description
* Normalizes the given path, returning a regular expression
* and the original path.
*
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
*/
function pathRegExp(path, opts) {
var insensitive = opts.caseInsensitiveMatch,
ret = {
originalPath: path,
regexp: path
},
keys = ret.keys = [];
path = path
.replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (star && '(.+?)' || '([^/]+)')
+ (optional || '')
+ ')'
+ (optional || '');
})
.replace(/([\/$\*])/g, '\\$1');
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
return ret;
}
/**
* @ngdoc method
* @name ngRoute.$routeProvider#otherwise
* @methodOf ngRoute.$routeProvider
*
* @description
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object} params Mapping information to be assigned to `$route.current`.
* @returns {Object} self
*/
this.otherwise = function(params) {
this.when(null, params);
return this;
};
this.$get = ['$rootScope',
'$location',
'$routeParams',
'$q',
'$injector',
'$http',
'$templateCache',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
/**
* @ngdoc object
* @name ngRoute.$route
* @requires $location
* @requires $routeParams
*
* @property {Object} current Reference to the current route definition.
* The route definition contains:
*
* - `controller`: The controller constructor as define in route definition.
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
* controller instantiation. The `locals` contain
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
*
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Array.<Object>} routes Array of all configured routes.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
* It watches `$location.url()` and tries to map the path to an existing route definition.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
*
* The `$route` service is typically used in conjunction with the
* {@link ngRoute.directive:ngView `ngView`} directive and the
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
This example shows how changing the URL hash causes the `$route` to match a route against the
URL, and the `ngView` pulls in the partial.
Note that this example is using {@link ng.directive:script inlined templates}
to get it working on jsfiddle as well.
<example module="ngViewExample" deps="angular-route.js">
<file name="index.html">
<div ng-controller="MainCntl">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div ng-view></div>
<hr />
<pre>$location.path() = {{$location.path()}}</pre>
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{$route.current.params}}</pre>
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
<pre>$routeParams = {{$routeParams}}</pre>
</div>
</file>
<file name="book.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
</file>
<file name="chapter.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
Chapter Id: {{params.chapterId}}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCntl,
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCntl
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
function MainCntl($scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
}
function BookCntl($scope, $routeParams) {
$scope.name = "BookCntl";
$scope.params = $routeParams;
}
function ChapterCntl($scope, $routeParams) {
$scope.name = "ChapterCntl";
$scope.params = $routeParams;
}
</file>
<file name="protractorTest.js">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeStart
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occur.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information.
* @param {Route} current Current route information.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeSuccess
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information.
* @param {Route|Undefined} previous Previous route information, or undefined if current is
* first route entered.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeError
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current route information.
* @param {Route} previous Previous route information.
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeUpdate
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
*
* The `reloadOnSearch` property has been set to false, and we are reusing the same
* instance of the Controller.
*/
var forceReload = false,
$route = {
routes: routes,
/**
* @ngdoc method
* @name ngRoute.$route#reload
* @methodOf ngRoute.$route
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*
* As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope, reinstantiates the controller.
*/
reload: function() {
forceReload = true;
$rootScope.$evalAsync(updateRoute);
}
};
$rootScope.$on('$locationChangeSuccess', updateRoute);
return $route;
/////////////////////////////////////////////////////
/**
* @param on {string} current url
* @param route {Object} route regexp to match the url against
* @return {?Object}
*
* @description
* Check if the route matches the current url.
*
* Inspired by match in
* visionmedia/express/lib/router/router.js.
*/
function switchRouteMatcher(on, route) {
var keys = route.keys,
params = {};
if (!route.regexp) return null;
var m = route.regexp.exec(on);
if (!m) return null;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
if (key && val) {
params[key.name] = val;
}
}
return params;
}
function updateRoute() {
var next = parseRoute(),
last = $route.current;
if (next && last && next.$$route === last.$$route
&& angular.equals(next.pathParams, last.pathParams)
&& !next.reloadOnSearch && !forceReload) {
last.params = next.params;
angular.copy(last.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', last);
} else if (next || last) {
forceReload = false;
$rootScope.$broadcast('$routeChangeStart', next, last);
$route.current = next;
if (next) {
if (next.redirectTo) {
if (angular.isString(next.redirectTo)) {
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
.replace();
} else {
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
.replace();
}
}
}
$q.when(next).
then(function() {
if (next) {
var locals = angular.extend({}, next.resolve),
template, templateUrl;
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value);
});
if (angular.isDefined(template = next.template)) {
if (angular.isFunction(template)) {
template = template(next.params);
}
} else if (angular.isDefined(templateUrl = next.templateUrl)) {
if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(next.params);
}
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
next.loadedTemplateUrl = templateUrl;
template = $http.get(templateUrl, {cache: $templateCache}).
then(function(response) { return response.data; });
}
}
if (angular.isDefined(template)) {
locals['$template'] = template;
}
return $q.all(locals);
}
}).
// after route change
then(function(locals) {
if (next == $route.current) {
if (next) {
next.locals = locals;
angular.copy(next.params, $routeParams);
}
$rootScope.$broadcast('$routeChangeSuccess', next, last);
}
}, function(error) {
if (next == $route.current) {
$rootScope.$broadcast('$routeChangeError', next, last, error);
}
});
}
}
/**
* @returns the current active route, by matching it against the URL
*/
function parseRoute() {
// Match a route
var params, match;
angular.forEach(routes, function(route, path) {
if (!match && (params = switchRouteMatcher($location.path(), route))) {
match = inherit(route, {
params: angular.extend({}, $location.search(), params),
pathParams: params});
match.$$route = route;
}
});
// No route matched; fallback to "otherwise" route
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
}
/**
* @returns interpolation of the redirect path with the parameters
*/
function interpolate(string, params) {
var result = [];
angular.forEach((string||'').split(':'), function(segment, i) {
if (i === 0) {
result.push(segment);
} else {
var segmentMatch = segment.match(/(\w+)(.*)/);
var key = segmentMatch[1];
result.push(params[key]);
result.push(segmentMatch[2] || '');
delete params[key];
}
});
return result.join('');
}
}];
}
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
/**
* @ngdoc object
* @name ngRoute.$routeParams
* @requires $route
*
* @description
* The `$routeParams` service allows you to retrieve the current set of route parameters.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
*
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
* (but its properties will likely change) even when a route change occurs.
*
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* <pre>
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
* </pre>
*/
function $RouteParamsProvider() {
this.$get = function() { return {}; };
}
ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
* @name ngRoute.directive:ngView
* @restrict ECA
*
* @description
* # Overview
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
* including the rendered template of the current route into the main layout (`index.html`) file.
* Every time the current route changes, the included view changes with it according to the
* configuration of the `$route` service.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
* @param {string=} onload Expression to evaluate whenever the view updates.
*
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the view is updated.
*
* - If the attribute is not set, disable scrolling.
* - If the attribute is set without value, enable scrolling.
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
* as an expression yields a truthy value.
* @example
<example module="ngViewExample" deps="angular-route.js" animations="true">
<file name="index.html">
<div ng-controller="MainCntl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div class="view-animate-container">
<div ng-view class="view-animate"></div>
</div>
<hr />
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
controller: {{book.name}}<br />
Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
controller: {{chapter.name}}<br />
Book Id: {{chapter.params.bookId}}<br />
Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
<file name="animations.css">
.view-animate-container {
position:relative;
height:100px!important;
position:relative;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.view-animate {
padding:10px;
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
width:100%;
border-left:1px solid black;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
padding:10px;
}
.view-animate.ng-enter {
left:100%;
}
.view-animate.ng-enter.ng-enter-active {
left:0;
}
.view-animate.ng-leave.ng-leave-active {
left:-100%;
}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCntl,
controllerAs: 'book'
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCntl,
controllerAs: 'chapter'
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
function MainCntl($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}
function BookCntl($routeParams) {
this.name = "BookCntl";
this.params = $routeParams;
}
function ChapterCntl($routeParams) {
this.name = "ChapterCntl";
this.params = $routeParams;
}
</file>
<file name="protractorTest.js">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngRoute.directive:ngView#$viewContentLoaded
* @eventOf ngRoute.directive:ngView
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory( $route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
priority: 400,
transclude: 'element',
link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope,
currentElement,
autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || '';
scope.$on('$routeChangeSuccess', update);
update();
function cleanupLastView() {
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement);
currentElement = null;
}
}
function update() {
var locals = $route.current && $route.current.locals,
template = locals && locals.$template;
if (angular.isDefined(template)) {
var newScope = scope.$new();
var current = $route.current;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-view on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});
currentElement = clone;
currentScope = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
cleanupLastView();
}
}
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;
$element.html(locals.$template);
var link = $compile($element.contents());
if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}
link(scope);
}
};
}
})(window, window.angular);
+249 -161
View File
@@ -1,15 +1,26 @@
/**
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* @license AngularJS v1.2.13
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
(function(window, angular, undefined) {'use strict';
var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc overview
* @name ngSanitize
* @description
*
* # ngSanitize
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
* {@installModule sanitize}
*
* <div doc-module-components="ngSanitize"></div>
*
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
*/
/*
@@ -40,97 +51,124 @@
* 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.
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
*
* @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>');
});
<doc:source>
<script>
function Ctrl($scope, $sce) {
$scope.snippet =
'<p style="color:blue">an html\n' +
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
'snippet</p>';
$scope.deliberatelyTrustDangerousSnippet = function() {
return $sce.trustAsHtml($scope.snippet);
};
}
</script>
<div ng-controller="Ctrl">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Directive</td>
<td>How</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="bind-html-with-sanitize">
<td>ng-bind-html</td>
<td>Automatically uses $sanitize</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="bind-html-with-trust">
<td>ng-bind-html</td>
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
<td>
<pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
&lt;/div&gt;</pre>
</td>
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
</tr>
<tr id="bind-default">
<td>ng-bind</td>
<td>Automatically escapes</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</div>
</doc:source>
<doc:protractor>
it('should sanitize the html snippet by default', function() {
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
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 bound to a trusted value', function() {
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
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 escape snippet without any filter', function() {
expect(element(by.css('#bind-default div')).getInnerHtml()).
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 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>
it('should update', function() {
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('new <b>text</b>');
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
'new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</doc:protractor>
</doc:example>
*/
var $sanitize = function(html) {
function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
return function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
}));
return buf.join('');
};
}];
}
function sanitizeText(chars) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf));
return buf.join('');
};
var writer = htmlSanitizeWriter(buf, angular.noop);
writer.chars(chars);
return buf.join('');
}
// Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
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,
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
// Good source of info about elements and attributes
@@ -145,23 +183,29 @@ var voidElements = makeMap("area,br,col,hr,img,wbr");
// 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);
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"));
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"));
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);
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");
@@ -169,7 +213,7 @@ 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,'+
'scope,scrolling,shape,size,span,start,summary,target,title,type,'+
'valign,value,vspace,width'));
function makeMap(str) {
@@ -203,14 +247,22 @@ function htmlParser( html, handler ) {
// Comment
if ( html.indexOf("<!--") === 0 ) {
index = html.indexOf("-->");
// comments containing -- are not allowed unless they terminate the comment
index = html.indexOf("--", 4);
if ( index >= 0 ) {
if ( index >= 0 && html.lastIndexOf("-->", index) === index) {
if (handler.comment) handler.comment( html.substring( 4, index ) );
html = html.substring( index + 3 );
chars = false;
}
// DOCTYPE
} else if ( DOCTYPE_REGEXP.test(html) ) {
match = html.match( DOCTYPE_REGEXP );
if ( match ) {
html = html.replace( match[0] , '');
chars = false;
}
// end tag
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
match = html.match( END_TAG_REGEXP );
@@ -242,21 +294,21 @@ function htmlParser( html, handler ) {
}
} else {
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){
text = text.
replace(COMMENT_REGEXP, "$1").
replace(CDATA_REGEXP, "$1");
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) );
if (handler.chars) handler.chars( decodeEntities(text) );
return "";
return "";
});
parseEndTag( "", stack.last() );
}
if ( html == last ) {
throw "Parse Error: " + html;
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
"of html: {0}", html);
}
last = html;
}
@@ -283,13 +335,14 @@ function htmlParser( html, handler ) {
var attrs = {};
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
var value = doubleQuotedValue
|| singleQoutedValue
|| unqoutedValue
|| '';
rest.replace(ATTR_REGEXP,
function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
var value = doubleQuotedValue
|| singleQuotedValue
|| unquotedValue
|| '';
attrs[name] = decodeEntities(value);
attrs[name] = decodeEntities(value);
});
if (handler.start) handler.start( tagName, attrs, unary );
}
@@ -314,15 +367,32 @@ function htmlParser( html, handler ) {
}
}
var hiddenPre=document.createElement("pre");
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
/**
* 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 || '';
if (!value) { return ''; }
// Note: IE8 does not preserve spaces at the start/end of innerHTML
// so we must capture them and reattach them afterward
var parts = spaceRe.exec(value);
var spaceBefore = parts[1];
var spaceAfter = parts[3];
var content = parts[2];
if (content) {
hiddenPre.innerHTML=content.replace(/</g,"&lt;");
// innerText depends on styling as it doesn't display hidden elements.
// Therefore, it's better to use textContent not to cause unnecessary
// reflows. However, IE<9 don't support textContent so the innerText
// fallback is necessary.
content = 'textContent' in hiddenPre ?
hiddenPre.textContent : hiddenPre.innerText;
}
return spaceBefore + content + spaceAfter;
}
/**
@@ -352,7 +422,7 @@ function encodeEntities(value) {
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf){
function htmlSanitizeWriter(buf, uriValidator){
var ignore = false;
var out = angular.bind(buf, buf.push);
return {
@@ -361,12 +431,14 @@ function htmlSanitizeWriter(buf){
if (!ignore && specialElements[tag]) {
ignore = tag;
}
if (!ignore && validElements[tag] == true) {
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))) {
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' ');
out(key);
out('="');
@@ -379,7 +451,7 @@ function htmlSanitizeWriter(buf){
},
end: function(tag){
tag = angular.lowercase(tag);
if (!ignore && validElements[tag] == true) {
if (!ignore && validElements[tag] === true) {
out('</');
out(tag);
out('>');
@@ -398,30 +470,9 @@ function htmlSanitizeWriter(buf){
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/**
* @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 || '');
});
};
}]);
/* global sanitizeText: false */
/**
* @ngdoc filter
@@ -429,10 +480,13 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
* @function
*
* @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @returns {string} Html-linkified text.
*
* @usage
@@ -449,6 +503,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
}
</script>
<div ng-controller="Ctrl">
@@ -468,6 +523,15 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></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>
@@ -475,45 +539,50 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
</tr>
</table>
</doc:source>
<doc:scenario>
<doc:protractor>
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>.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
});
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 not linkify snippet without the linky filter', function() {
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
});
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.');
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new http://link.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('new http://link.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
.toBe('new http://link.');
});
</doc:scenario>
it('should work with the target property', function() {
expect(element(by.id('linky-target')).
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
</doc:protractor>
</doc:example>
*/
angular.module('ngSanitize').filter('linky', function() {
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
MAILTO_REGEXP = /^mailto:/;
return function(text) {
return function(text, target) {
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))) {
@@ -522,16 +591,35 @@ angular.module('ngSanitize').filter('linky', function() {
// 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');
addText(raw.substr(0, i));
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
raw = raw.substring(i + match[0].length);
}
writer.chars(raw);
return html.join('');
addText(raw);
return $sanitize(html.join(''));
function addText(text) {
if (!text) {
return;
}
html.push(sanitizeText(text));
}
function addLink(url, text) {
html.push('<a ');
if (angular.isDefined(target)) {
html.push('target="');
html.push(target);
html.push('" ');
}
html.push('href="');
html.push(url);
html.push('">');
addText(text);
html.push('</a>');
}
};
});
}]);
})(window, window.angular);
File diff suppressed because it is too large. Load diff
+12048 -6021
View File
File diff suppressed because it is too large. Load diff
@@ -1,195 +1,222 @@
/**
* 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')));
});
};
}
};
}]);
/**
* 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',
priority: 1,
compile: function (tElm, tAttrs) {
var watch,
repeatOption,
repeatAttr,
isSelect = tElm.is('select'),
isMultiple = angular.isDefined(tAttrs.multiple);
// Enable watching of the options dataset if in use
if (tElm.is('select')) {
repeatOption = tElm.find( 'optgroup[ng-repeat], optgroup[data-ng-repeat], 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) {
var viewValue = controller.$viewValue;
if (angular.isString(viewValue)) {
viewValue = viewValue.split(',');
}
elm.select2(
'data', convertToSelect2Model(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 (angular.equals(newVal, oldVal)) {
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');
if(newVal && !oldVal && controller.$setPristine) {
controller.$setPristine(true);
}
});
});
}
// 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 (e) {
e.stopImmediatePropagation();
if (scope.$$phase || scope.$root.$$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) {
var isPristine = controller.$pristine;
controller.$setViewValue(convertToAngularModel(value));
callback(value);
if (isPristine) {
controller.$setPristine();
}
elm.prev().toggleClass('ng-pristine', controller.$pristine);
});
};
}
}
}
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) {
attrs.$set('multiple', !!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.select2('data', controller.$modelValue);
// important!
controller.$render();
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect) {
var isPristine = controller.$pristine;
controller.$setViewValue(
convertToAngularModel(elm.select2('data'))
);
if (isPristine) {
controller.$setPristine();
}
elm.prev().toggleClass('ng-pristine', controller.$pristine);
}
});
};
}
};
}]);
@@ -0,0 +1,29 @@
<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><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">{{application.name}}</a></li>
<li class="active">Claims</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Allowed Claims</h2>
<form class="form-horizontal" name="claimForm">
<div data-ng-include data-src="'partials/claims.html'"></div>
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>
</div>
@@ -5,8 +5,10 @@
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
@@ -16,7 +18,7 @@
<li class="active">Credentials</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageApplications">
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
@@ -27,7 +29,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>
@@ -2,11 +2,13 @@
<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}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-show="create">
@@ -23,7 +25,7 @@
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Settings</h2>
<form class="form-horizontal" name="applicationForm" novalidate>
<form class="form-horizontal" name="applicationForm" novalidate kc-read-only="!access.manageApplications">
<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>
@@ -37,6 +39,44 @@
<input ng-model="application.enabled" name="enabled" id="enabled" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="clientType">Client Type</label>
<div class="col-sm-4">
<div class="select-kc">
<select id="clientType"
ng-change="changeClientType()"
ng-model="clientType"
ng-options="cType for cType in clientTypes">
</select>
</div>
</div>
</div>
<div class="form-group" data-ng-show="!application.bearerOnly">
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
<div class="col-sm-6">
<div ng-repeat="redirectUri in application.redirectUris" class="row kc-item-deletable">
<div class="col-sm-8">
<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-8">
<input class="form-control" type="text" name="newRedirectUri" id="newRedirectUri"
placeholder="New Redirect URI..." data-ng-model="newRedirectUri"
data-ng-class="{'input-below':application.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>
<div class="form-group">
<label class="col-sm-2 control-label" for="baseUrl">Base URL</label>
<div class="col-sm-4">
@@ -53,9 +93,9 @@
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
<div class="col-sm-4">
<div class="col-sm-6">
<div ng-repeat="webOrigin in application.webOrigins" class="row kc-item-deletable">
<div class="col-sm-9">
<div class="col-sm-8">
<input class="form-control kc-button-control"
type="text" data-ng-class="{'input-below':!$first}"
name="webOrigin" id="webOrigin"
@@ -67,7 +107,7 @@
</div>
</div>
<div class="row">
<div class="col-sm-9">
<div class="col-sm-8">
<input class="form-control kc-button-control"
type="text" name="newWebOrigin" id="newWebOrigin"
placeholder="New Web Origin..." data-ng-model="newWebOrigin"
@@ -80,39 +120,13 @@
</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 application.redirectUris" class="row kc-item-deletable">
<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':application.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">
<div class="pull-right form-actions" data-ng-show="create && access.manageApplications">
<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">
<div class="pull-right form-actions" data-ng-show="!create && access.manageApplications">
<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>
@@ -3,19 +3,22 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div class="top-nav" data-ng-show="create">
<ul class="rcue-tabs">
<li></li>
</ul>
</div>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">{{application.name}}</a></li>
<li class="active">Installation</li>
</ol>
<h2>Application Installation</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
@@ -32,7 +32,7 @@
<th>Base URL</th>
</tr>
</thead>
<tfoot data-ng-show="applications && applications.length > 5"> <!-- todo -->
<!--<tfoot data-ng-show="applications && applications.length > 5">
<tr>
<td colspan="3">
<div class="table-nav">
@@ -43,7 +43,7 @@
</div>
</td>
</tr>
</tfoot>
</tfoot>-->
<tbody>
<tr ng-repeat="app in applications | filter:search">
<td><a href="#/realms/{{realm.realm}}/applications/{{app.name}}">{{app.name}}</a></td>
@@ -0,0 +1,38 @@
<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><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/">{{realm.realm}}</a></li>
<li class="active">Revocation</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Revocation Policies</h2>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="notBefore">Not Before</label>
<div class="col-sm-4">
<input ng-disabled="true" class="form-control" type="text" id="notBefore" name="notBefore" data-ng-model="notBefore" autofocus>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button type="submit" data-ng-click="clear()" class="btn btn-default btn-lg">Clear
</button>
<button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-primary btn-lg">Set To Now
</button>
<button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary btn-lg">Push
</button>
</div>
</form>
</div>
</div>
@@ -2,11 +2,13 @@
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
@@ -29,7 +31,7 @@
<h2 data-ng-hide="create"><span>{{application.name}}</span> {{role.name}}</h2>
<h2 data-ng-show="create"><span>{{application.name}}</span> Add Role</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageApplications">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset class="border-top">
@@ -134,11 +136,11 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageApplications">
<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">
<div class="pull-right form-actions" data-ng-show="!create && access.manageApplications">
<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>
@@ -3,11 +3,13 @@
<ul class="nav nav-tabs nav-tabs-pf">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
@@ -21,14 +23,14 @@
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageApplications">
<div class="pull-right">
<a class="btn btn-primary" href="#/create/role/{{realm.realm}}/applications/{{application.name}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->
</div>
</th>
</tr>
<tr data-ng-show="roles && roles.length > 5">
<!--<tr data-ng-show="roles && roles.length > 5">
<th class="kc-table-actions" colspan="3">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
@@ -39,13 +41,14 @@
</div>
</th>
</tr>
-->
<tr data-ng-hide="!roles || roles.length == 0">
<th>Role Name</th>
<th>Composite</th>
<th>Description</th>
</tr>
</thead>
<tfoot data-ng-show="roles && roles.length > 5"> <!-- todo -->
<!--<tfoot data-ng-show="roles && roles.length > 5">
<tr>
<td colspan="3">
<div class="table-nav">
@@ -57,6 +60,7 @@
</td>
</tr>
</tfoot>
-->
<tbody>
<tr ng-repeat="role in roles">
<td><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles/{{role.name}}">{{role.name}}</a></td>
@@ -3,11 +3,13 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
@@ -19,7 +21,7 @@
</ol>
<h2><span>{{application.name}}</span> Scope Mappings</h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageApplications">
<fieldset>
<legend><span class="text">Realm Roles</span></legend>
<div class="form-group col-sm-10">
@@ -59,7 +61,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications|remove:application:'id')">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications|remove:application:'id')" ng-disabled="false">
<option value="" selected> Select an Application </option>
</select>
</div>
@@ -1,22 +1,60 @@
<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">
<ul class="nav nav-tabs nav-tabs-pf">
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">{{application.name}}</a></li>
<li class="active">Sessions</li>
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{application.name}}</a></li>
<li class="active">Application Sessions</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Sessions (Placeholder Page)</h2>
<h2><span>{{application.name}}</span> Sessions</h2>
<form class="form-horizontal" name="sessionStats">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="activeSessions">Active Sessions</label>
<div class="col-sm-4">
<input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="stats.activeSessions" ng-disabled="true">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="activeUsers">Active Users</label>
<div class="col-sm-4">
<input class="form-control" type="text" id="activeUsers" name="activeUsers" data-ng-model="stats.activeUsers" ng-disabled="true">
</div>
</div>
</fieldset>
</form>
<table class="table" data-ng-show="stats.activeSessions > 0">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
<a class="btn btn-primary" ng-click="logoutAll()">Invalidate All Sessions</a>
<a class="btn btn-primary" ng-click="loadUsers()">Show Users</a>
</div>
</th>
</tr>
<tr>
<th>User</th>
<th>Login Time</th>
<th></th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="(user, data) in users">
<td><a href="#/realms/{{realm.realm}}/users/{{user}}">{{user}}</a></td>
<td>{{data.whenLoggedIn | date:'medium'}}</td>
<td><a ng-click="logoutUser(user)">invalidate session</a> </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
@@ -0,0 +1,62 @@
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="username">Username</label>
<div class="col-sm-4">
<input ng-model="claims.username" name="username" id="username" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="claimName">Name</label>
<div class="col-sm-4">
<input ng-model="claims.name" name="claimName" id="claimName" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="email">Email</label>
<div class="col-sm-4">
<input ng-model="claims.email" name="email" id="email" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="gender">Gender</label>
<div class="col-sm-4">
<input ng-model="claims.gender" name="gender" id="gender" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="address">Address</label>
<div class="col-sm-4">
<input ng-model="claims.address" name="address" id="address" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="locale">Locale</label>
<div class="col-sm-4">
<input ng-model="claims.locale" name="locale" id="locale" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="phone">Phone</label>
<div class="col-sm-4">
<input ng-model="claims.phone" name="phone" id="phone" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="profile">Profile URL</label>
<div class="col-sm-4">
<input ng-model="claims.profile" name="profile" id="profile" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="picture">Picture URL</label>
<div class="col-sm-4">
<input ng-model="claims.picture" name="picture" id="picture" onoffswitch />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="website">Website</label>
<div class="col-sm-4">
<input ng-model="claims.website" name="website" id="website" onoffswitch />
</div>
</div>
</fieldset>
@@ -1,43 +1,47 @@
<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 class="container">
<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">Keycloak</h1>
</div>
</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 class="container">
<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/{{auth.user.realm}}/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 && access.createRealm">
<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>
</div>
</nav>
<!-- TODO remove once this page is properly styled -->
@@ -0,0 +1,21 @@
<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">
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
<li data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</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>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Allowed Claims</h2>
<form class="form-horizontal" name="claimForm">
<div data-ng-include data-src="'partials/claims.html'"></div>
<div class="pull-right form-actions" data-ng-show="access.manageClients">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>
</div>
@@ -3,8 +3,10 @@
<ul class="nav nav-tabs nav-tabs-pf">
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</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>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
@@ -15,7 +17,7 @@
<li class="active">Credentials</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
@@ -26,7 +28,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageClients">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>
@@ -2,9 +2,11 @@
<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 data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</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>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
<ol class="breadcrumb" data-ng-show="create">
@@ -21,7 +23,7 @@
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Settings</h2>
<form class="form-horizontal" name="oauthForm" novalidate>
<form class="form-horizontal" name="oauthForm" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group">
@@ -39,35 +41,22 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
<label class="col-sm-2 control-label" for="clientType">Client Type</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 class="select-kc">
<select id="clientType"
ng-change="changeClientType()"
ng-model="clientType"
ng-options="cType for cType in clientTypes">
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI</label>
<div class="col-sm-4">
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
<div class="col-sm-6">
<div ng-repeat="redirectUri in oauth.redirectUris" class="kc-item-deletable row">
<div class="col-sm-9">
<div class="col-sm-8">
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
name="redirectUri" id="redirectUri" data-ng-model="redirectUri" readonly />
</div>
@@ -77,7 +66,7 @@
</div>
</div>
<div class="row">
<div class="col-sm-9">
<div class="col-sm-8">
<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}" />
@@ -88,13 +77,38 @@
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
<div class="col-sm-6">
<div ng-repeat="webOrigin in oauth.webOrigins" class="kc-item-deletable row">
<div class="col-sm-8">
<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-8">
<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>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageClients">
<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">
<div class="pull-right form-actions" data-ng-show="!create && access.manageClients">
<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>
@@ -2,9 +2,11 @@
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><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 data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
<h2>OAuth Client Installation</h2>
@@ -14,15 +14,15 @@
<tr>
<th class="kc-table-actions" colspan="3">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="form-control search"
<input type="text" placeholder="Search..." class="form-control search" data-ng-model="search.name"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button class="kc-icon-search" tooltip-placement="right"
tooltip="Search by oauth client name">
<button type="submit" class="kc-icon-search" tooltip-placement="right"
tooltip="Search by application name.">
Icon: search
</button>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="#/create/oauth-client/{{realm.realm}}">Add OAuth Client</a>
<a class="btn btn-primary" href="#/create/application/{{realm.realm}}">Add Application</a>
</div>
</th>
</tr>
@@ -31,7 +31,7 @@
<th>Enabled</th>
</tr>
</thead>
<tfoot data-ng-show="oauthClients && oauthClients.length > 5"> <!-- todo -->
<!--<tfoot data-ng-show="oauthClients && oauthClients.length > 5">
<tr>
<td colspan="2">
<div class="table-nav">
@@ -43,8 +43,9 @@
</td>
</tr>
</tfoot>
-->
<tbody>
<tr ng-repeat="client in oauthClients">
<tr ng-repeat="client in oauthClients | filter:search">
<td><a href="#/realms/{{realm.realm}}/oauth-clients/{{client.id}}">{{client.name}}</a></td>
<td>{{client.enabled}}</td>
</tr>
@@ -0,0 +1,34 @@
<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><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
<li data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</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>
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li class="active">Revocation</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Revocation Policies</h2>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="notBefore">Not Before</label>
<div class="col-sm-4">
<input ng-disabled="true" class="form-control" type="text" id="notBefore" name="notBefore" data-ng-model="notBefore" autofocus>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button type="submit" data-ng-click="clear()" class="btn btn-default btn-lg">Clear
</button>
<button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-primary btn-lg">Set To Now
</button>
</div>
</form>
</div>
</div>
@@ -3,9 +3,11 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><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 data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</a></li>
<li class="active"><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>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
</ul>
<div id="content">
@@ -17,7 +19,7 @@
</ol>
<h2><span>{{oauth.name}}</span> Scope Mappings</h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
<fieldset>
<legend><span class="text">Realm Roles</span></legend>
<div class="form-group col-sm-10">
@@ -57,7 +59,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications)">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications)" ng-disabled="false">
<option value="" selected> Select an Application </option>
</select>
</div>
@@ -1,7 +1,7 @@
<div class="bs-sidebar col-sm-3 "></div>
<div id="content-area" class="col-sm-9" role="main">
<div id="content">
<h2>Add Realm</h2>
<h2 class="margin-top">Add Realm</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
@@ -9,9 +9,9 @@
<li class="active">Required Credentials</li>
</ol>
<h2><span>{{realm.realm}}</span> Credentials</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<legend uncollapsed><span class="text">Realm Credentials Settings</span></legend>
<legend><span class="text">Realm Credentials Settings</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="user" class="control-label two-lines">Required User Credentials</label>
@@ -21,7 +21,7 @@
</div>
</fieldset>
<fieldset class="border-top">
<legend uncollapsed><span class="text">Realm Password Policy</span></legend>
<legend><span class="text">Realm Password Policy</span></legend>
<table class="table">
<caption class="hidden">Table of Password Policies</caption>
<thead>
@@ -63,7 +63,7 @@
</tbody>
</table>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
@@ -8,7 +8,7 @@
<li class="active">Registration</li>
</ol>
<h2><span>{{realm.realm}}</span> Default Roles</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<legend><span class="text">Realm Default Roles</span> </legend>
<div class="form-group">
@@ -48,7 +48,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>
@@ -7,98 +7,108 @@
<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 data-ng-show="access.viewRealm">
<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 kc-read-only="!access.manageRealm">
<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 class="col-sm-4">
<input class="form-control" type="text" id="name" name="name" data-ng-model="realm.realm" autofocus required>
</div>
</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 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>
</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 />
</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>
<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 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>
<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 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>
<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 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>
<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 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>
<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 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>
</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>
</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">
<div class="select-kc">
<select id="loginTheme"
ng-model="realm.loginTheme"
ng-options="o as o for o in serverInfo.themes.login">
<option value="" disabled selected>Select one...</option>
</select>
</div>
</div>
</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 class="form-group">
<label class="col-sm-2 control-label" for="accountTheme">Account Theme</label>
<div class="col-sm-4">
<div class="select-kc">
<select id="accountTheme"
ng-model="realm.accountTheme"
ng-options="o as o for o in serverInfo.themes.account">
<option value="" disabled selected>Select one...</option>
</select>
</div>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="createRealm && access.manageRealm">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="!createRealm && access.manageRealm">
<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 data-ng-hide="access.viewRealm">
<h2 ><span>{{realm.realm}}</span></h2>
</div>
<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>
</div>
@@ -8,7 +8,7 @@
<li class="active">Keys</li>
</ol>
<h2><span>{{realm.realm}}</span> Keys</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
@@ -19,7 +19,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
</div>
</form>
@@ -1,34 +1,25 @@
<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 class="bs-sidebar col-sm-3 ">
<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">
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="">Realm List</a></li>
</ul>
<div id="content">
<h2><span>Realms</span></h2>
<table class="table">
<thead>
<tr>
<th>Realm</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="r in realms">
<td><a href="#/realms/{{r.realm}}">{{r.realm}}</a></td>
</tr>
</tbody>
</table>
</div>
</div>
@@ -1,10 +1,10 @@
<ul data-ng-hide="createRealm">
<li data-ng-class="((!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' ||
<li data-ng-show="access.viewRealm" 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 data-ng-show="access.viewUsers" 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>
<li data-ng-show="access.viewApplications" 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-show="access.viewClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/revocation">Sessions</a></li>
</ul>
@@ -8,10 +8,10 @@
<li class="active">SMTP Configuration</li>
</ol>
<h2><span>{{realm.realm}}</span> Email Server Settings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset>
<legend uncollapsed><span class="text">Required Settings</span></legend>
<legend><span class="text">Required Settings</span></legend>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="smtpHost">Host <span class="required">*</span></label>
<div class="col-sm-4">
@@ -44,7 +44,7 @@
</div>
</fieldset>
<fieldset>
<legend uncollapsed><span class="text">Authentication</span></legend>
<legend><span class="text">Authentication</span></legend>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="smtpAuth">Enable Authentication</label>
<div class="col-sm-4">
@@ -65,7 +65,7 @@
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button data-kc-reset data-ng-show="changed">Clear changes</button>
<button data-kc-save data-ng-show="changed">Save</button>
</div>
@@ -1,76 +1,122 @@
<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">
<data-kc-navigation data-kc-current="token" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li class="active">Token</li>
</ol>
<h2><span>{{realm.realm}}</span> Token Settings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset class="border-top">
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="tokenLifespan">Token lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1"
max="31536000" data-ng-model="realm.tokenLifespan"
id="tokenLifespan" name="tokenLifespan"/>
</div>
<div class="col-sm-2 select-kc">
<select name="tokenLifespanUnit" data-ng-model="realm.tokenLifespanUnit" >
<option data-ng-selected="!realm.tokenLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="accessCodeLifespan">Access code lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1" max="31536000" data-ng-model="realm.accessCodeLifespan" id="accessCodeLifespan" name="accessCodeLifespan">
</div>
<div class="col-sm-2 select-kc">
<select name="accessCodeLifespanUnit" data-ng-model="realm.accessCodeLifespanUnit">
<option data-ng-selected="!realm.accessCodeLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="accessCodeLifespanUserAction" class="two-lines">Access code user action lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1" max="31536000" data-ng-model="realm.accessCodeLifespanUserAction" id="accessCodeLifespanUserAction" name="accessCodeLifespanUserAction">
</div>
<div class="col-sm-2 select-kc">
<select name="accessCodeLifespanUserActionUnit" data-ng-model="realm.accessCodeLifespanUserActionUnit">
<option data-ng-selected="!realm.accessCodeLifespanUserActionUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>
<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">
<data-kc-navigation data-kc-current="token" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li class="active">Token</li>
</ol>
<h2><span>{{realm.realm}}</span> Token Settings</h2>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="rememberMe">Remember Me</label>
<div class="col-sm-4">
<input ng-model="realm.rememberMe" name="rememberMe" id="rememberMe" onoffswitch />
</div>
</div>
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="centralLoginLifespan">Central Login Lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1"
max="31536000" data-ng-model="realm.centralLoginLifespan"
id="centralLoginLifespan" name="centralLoginLifespan"/>
</div>
<div class="col-sm-2 select-kc">
<select name="centralLoginLifespanUnit" data-ng-model="realm.centralLoginLifespanUnit" >
<option data-ng-selected="!realm.centralLoginLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="accessTokenLifespan">Access Token Lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1"
max="31536000" data-ng-model="realm.accessTokenLifespan"
id="accessTokenLifespan" name="accessTokenLifespan"/>
</div>
<div class="col-sm-2 select-kc">
<select name="accessTokenLifespanUnit" data-ng-model="realm.accessTokenLifespanUnit" >
<option data-ng-selected="!realm.accessTokenLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="accessCodeLifespan">Access code lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1" max="31536000" data-ng-model="realm.accessCodeLifespan" id="accessCodeLifespan" name="accessCodeLifespan">
</div>
<div class="col-sm-2 select-kc">
<select name="accessCodeLifespanUnit" data-ng-model="realm.accessCodeLifespanUnit">
<option data-ng-selected="!realm.accessCodeLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="accessCodeLifespanUserAction" class="two-lines">Access code user action lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1" max="31536000" data-ng-model="realm.accessCodeLifespanUserAction" id="accessCodeLifespanUserAction" name="accessCodeLifespanUserAction">
</div>
<div class="col-sm-2 select-kc">
<select name="accessCodeLifespanUserActionUnit" data-ng-model="realm.accessCodeLifespanUserActionUnit">
<option data-ng-selected="!realm.accessCodeLifespanUserActionUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="refreshTokenLifespan">Refresh token lifespan</label>
<div class="col-sm-10">
<div class="row">
<div class="col-sm-2">
<input class="form-control" type="number" required min="1"
max="31536000" data-ng-model="realm.refreshTokenLifespan"
id="refreshTokenLifespan" name="refreshTokenLifespan"/>
</div>
<div class="col-sm-2 select-kc">
<select name="refreshTokenLifespanUnit" data-ng-model="realm.refreshTokenLifespanUnit" >
<option data-ng-selected="!realm.refreshTokenLifespanUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>
</div>
@@ -28,7 +28,7 @@
</ol>
<h2 data-ng-show="create"><span>{{realm.realm}}</span> Add Role</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Role name <span class="required" data-ng-show="create">*</span></label>
@@ -96,7 +96,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="compositeApp" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="compositeApp" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>
@@ -133,7 +133,7 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
<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>
@@ -13,22 +13,18 @@
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
<a class="btn btn-primary" href="#/create/role/{{realm.realm}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->
</div>
</th>
</tr>
<tr data-ng-show="roles && roles.length > 5">
<th class="kc-table-actions" colspan="3">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
<button class="icon-search" tooltip-placement="right"
tooltip="Search by role name.">
<input type="text" placeholder="Search..." data-ng-model="searchQuery" class="form-control search">
<button class="kc-icon-search" tooltip-placement="right"
tooltip="Search by name.">
Icon: search
</button>
</div>
<div class="pull-right" data-ng-show="access.manageRealm">
<a class="btn btn-primary" href="#/create/role/{{realm.realm}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->
</div>
</th>
</tr>
<tr data-ng-show="roles && roles.length > 0">
@@ -37,20 +33,21 @@
<th>Description</th>
</tr>
</thead>
<tfoot data-ng-show="roles && roles.length > 5"> <!-- todo -->
<!--
<tfoot data-ng-show="roles && roles.length > 5">
<tr>
<td colspan="3">
<div class="table-nav">
<a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
class="next">Next
page</a><a href="#" class="last">Last page</a>
<a href="#" class="first disabled">First page</a>
<a href="#" class="prev disabled">Previous page</a><span><strong>1-8</strong> of <strong>10</strong></span>
<a href="#" class="next">Next page</a><a href="#" class="last">Last page</a>
</div>
</td>
</tr>
</tfoot>
-->
<tbody>
<tr ng-repeat="role in roles">
<tr ng-repeat="role in roles | filter:{name: searchQuery}">
<td><a href="#/realms/{{realm.realm}}/roles/{{role.name}}">{{role.name}}</a></td>
<td>{{role.composite}}</td>
<td>{{role.description}}</td>
@@ -2,8 +2,9 @@
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
@@ -13,7 +14,7 @@
<li class="active">Role Mappings</li>
</ol>
<h2><span>{{user.username}}'s</span> Role Mappings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
<fieldset>
<legend><span class="text">Realm Roles</span> </legend>
@@ -54,7 +55,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>
@@ -0,0 +1,37 @@
<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">
<li><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li class="active">Realm Sessions</li>
</ol>
<h2><span>{{realm.realm}}</span> Sessions</h2>
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
<a class="btn btn-primary" ng-click="logoutAll()">Logout All</a>
</div>
</th>
</tr>
<tr>
<th>Application</th>
<th>Active Sessions</th>
<th>Active Users</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="(application, data) in stats">
<td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
<td>{{data.activeSessions}}</td>
<td>{{data.activeUsers}}</td>
</tr>
</tbody>
</table>
</div>
</div>
@@ -0,0 +1,32 @@
<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}}/sessions/revocation">Revocation</a></li>
<li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li class="active">Revocation</li>
</ol>
<h2 data-ng-hide="create"><span>{{realm.realm}}</span> Revocation Policies</h2>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="notBefore">Not Before</label>
<div class="col-sm-4">
<input ng-disabled="true" class="form-control" type="text" id="notBefore" name="notBefore" data-ng-model="notBefore" autofocus>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button type="submit" data-ng-click="clear()" class="btn btn-default btn-lg">Clear
</button>
<button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-primary btn-lg">Set To Now
</button>
<button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary btn-lg">Push
</button>
</div>
</form>
</div>
</div>
@@ -5,6 +5,7 @@
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
</ul>
<div id="content">
@@ -3,8 +3,9 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
</ul>
<div id="content">
@@ -22,7 +23,7 @@
</ol>
<h2 data-ng-hide="create"><span>{{user.username}}'s</span> Attributes</h2>
<form class="form-horizontal" name="userForm" novalidate>
<form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset class="border-top">
<div class="form-group">
@@ -75,16 +76,18 @@
<label class="col-sm-2 control-label" for="reqActions">Required User Actions</label>
<div class="col-sm-4">
<input type="text" ui-select2="userReqActionsOptions" ng-model="userActions" id="reqActions"/>
<select ui-select2 ng-model="user.requiredActions" data-placeholder="Select an action..." multiple>
<option ng-repeat="action in userReqActionList" value="{{action.id}}">{{action.text}}</option>
</select>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
<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">
<div class="pull-right form-actions" data-ng-show="!create && access.manageUsers">
<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>
@@ -0,0 +1,41 @@
<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">
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">{{user.username}}</a></li>
<li class="active">User Sessions</li>
</ol>
<h2><span>{{user.username}}</span> Sessions</h2>
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
<a class="btn btn-primary" ng-click="logoutAll()">Logout</a>
</div>
</th>
</tr>
<tr>
<th>Application</th>
<th>Login Time</th>
<th></th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="(application, data) in stats">
<td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
<td>{{data.whenLoggedIn | date:'medium'}}</td>
<td><a ng-click="logoutApplication(application)">invalidate session</a> </td>
</tr>
</tbody>
</table>
</div>
</div>
@@ -1,10 +1,10 @@
<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>
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
<li ng-class="{active: path[2] == 'social'}" data-ng-show="realm.social && access.viewRealm"><a href="#/realms/{{realm.realm}}/social-settings">Social</a></li>
<li ng-class="{active: path[2] == 'roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li>
<li ng-class="{active: path[2] == 'default-roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/default-roles">Default Roles</a></li>
<li ng-class="{active: path[2] == 'required-credentials'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/required-credentials">Credentials</a></li>
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">Token</a></li>
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
</ul>
@@ -0,0 +1,12 @@
<span>
<div class='onoffswitch' tabindex='0'>
<input type='checkbox' ng-model='ngModel' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'>
<label for='{{id}}' class='onoffswitch-label'>
<span class='onoffswitch-inner'>
<span class='onoffswitch-active'>{{kcOnText}}</span>
<span class='onoffswitch-inactive'>{{kcOffText}}</span>
</span>
<span class='onoffswitch-switch'></span>
</label>
</div>
</span>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -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);
}
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2</version>
<version>1.0-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -15,13 +15,14 @@ public class AbstractOAuthClient {
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
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 boolean publicClient;
protected final AtomicLong counter = new AtomicLong();
protected String getStateCode() {
@@ -44,20 +45,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 +61,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,6 +93,14 @@ 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)
@@ -1,16 +1,17 @@
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 SkeletonKeyPrincipal implements Principal {
public class KeycloakPrincipal implements Principal, Serializable {
protected String name;
protected String surrogate;
public SkeletonKeyPrincipal(String name, String surrogate) {
public KeycloakPrincipal(String name, String surrogate) {
this.name = name;
this.surrogate = surrogate;
}
@@ -29,7 +30,7 @@ public class SkeletonKeyPrincipal implements Principal {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SkeletonKeyPrincipal that = (SkeletonKeyPrincipal) o;
KeycloakPrincipal that = (KeycloakPrincipal) o;
if (!name.equals(that.name)) return false;
if (surrogate != null ? !surrogate.equals(that.surrogate) : that.surrogate != null) return false;
@@ -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;
}
}
+62
View File
@@ -0,0 +1,62 @@
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);
}
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,8 +12,12 @@ 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 {
@@ -23,15 +27,12 @@ public class RSATokenVerifier {
}
if (!verified) throw new VerificationException("Token signature not validated");
SkeletonKeyToken token = null;
AccessToken token = null;
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.");
}
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null");
@@ -40,6 +41,10 @@ public class RSATokenVerifier {
throw new VerificationException("Token audience doesn't match domain");
}
if (checkActive && !token.isActive()) {
throw new VerificationException("Token is not active.");
}
return token;
}
}
@@ -8,4 +8,5 @@ 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_REFRESH_PATH = "/rest/realms/{realm-name}/tokens/refresh";
}
@@ -1,38 +0,0 @@
package org.keycloak;
import org.keycloak.adapters.ResourceMetadata;
import org.keycloak.representations.SkeletonKeyToken;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SkeletonKeySession implements Serializable {
protected String tokenString;
protected SkeletonKeyToken token;
protected transient ResourceMetadata metadata;
public SkeletonKeySession() {
}
public SkeletonKeySession(String tokenString, SkeletonKeyToken token, ResourceMetadata metadata) {
this.tokenString = tokenString;
this.token = token;
this.metadata = metadata;
}
public SkeletonKeyToken getToken() {
return token;
}
public String getTokenString() {
return tokenString;
}
public ResourceMetadata getMetadata() {
return metadata;
}
}
@@ -8,6 +8,9 @@ public interface AdapterConstants {
// URL endpoints
public static final String K_LOGOUT = "k_logout";
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
public static final String K_GET_USER_STATS = "k_get_user_stats";
public static final String K_GET_SESSION_STATS = "k_get_session_stats";
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
// This param name is defined again in Keycloak Subsystem class
@@ -1,90 +0,0 @@
package org.keycloak.adapters;
import java.security.KeyStore;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceMetadata {
protected String realm;
protected String resourceName;
protected KeyStore clientKeystore;
protected String clientKeyPassword;
protected KeyStore truststore;
protected PublicKey realmKey;
protected String scope;
public String getResourceName() {
return resourceName;
}
public String getRealm() {
return realm;
}
/**
* keystore that contains service's private key and certificate.
* Used when making invocations on remote HTTPS endpoints that require client-cert authentication
*
* @return
*/
public KeyStore getClientKeystore() {
return clientKeystore;
}
public String getClientKeyPassword() {
return clientKeyPassword;
}
public void setClientKeyPassword(String clientKeyPassword) {
this.clientKeyPassword = clientKeyPassword;
}
/**
* Truststore to use if this service makes client invocations on remote HTTPS endpoints.
*
* @return
*/
public KeyStore getTruststore() {
return truststore;
}
/**
* Public key of the realm. Used to verify access tokens
*
* @return
*/
public PublicKey getRealmKey() {
return realmKey;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
public void setRealm(String realm) {
this.realm = realm;
}
public void setClientKeystore(KeyStore clientKeystore) {
this.clientKeystore = clientKeystore;
}
public void setTruststore(KeyStore truststore) {
this.truststore = truststore;
}
public void setRealmKey(PublicKey realmKey) {
this.realmKey = realmKey;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
@@ -12,13 +12,26 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SkeletonKeyToken extends JsonWebToken {
public class AccessToken extends IDToken {
public static class Access {
@JsonProperty("roles")
protected Set<String> roles;
@JsonProperty("verify_caller")
protected Boolean verifyCaller;
public Access() {
}
public Access clone() {
Access access = new Access();
access.verifyCaller = verifyCaller;
if (roles != null) {
access.roles = new HashSet<String>();
access.roles.addAll(roles);
}
return access;
}
public Set<String> getRoles() {
return roles;
}
@@ -50,9 +63,6 @@ public class SkeletonKeyToken extends JsonWebToken {
}
}
@JsonProperty("issuedFor")
public String issuedFor;
@JsonProperty("trusted-certs")
protected Set<String> trustedCertificates;
@@ -69,6 +79,13 @@ public class SkeletonKeyToken extends JsonWebToken {
return resourceAccess;
}
public void setResourceAccess(Map<String, Access> resourceAccess) {
this.resourceAccess = resourceAccess;
}
/**
* Does the realm require verifying the caller?
*
@@ -106,43 +123,44 @@ public class SkeletonKeyToken extends JsonWebToken {
}
@Override
public SkeletonKeyToken id(String id) {
return (SkeletonKeyToken) super.id(id);
public AccessToken id(String id) {
return (AccessToken) super.id(id);
}
@Override
public SkeletonKeyToken expiration(long expiration) {
return (SkeletonKeyToken) super.expiration(expiration);
public AccessToken expiration(long expiration) {
return (AccessToken) super.expiration(expiration);
}
@Override
public SkeletonKeyToken notBefore(long notBefore) {
return (SkeletonKeyToken) super.notBefore(notBefore);
public AccessToken notBefore(long notBefore) {
return (AccessToken) super.notBefore(notBefore);
}
@Override
public AccessToken issuedAt(int issuedAt) {
return (AccessToken) super.issuedAt(issuedAt);
}
@Override
public SkeletonKeyToken issuedAt(long issuedAt) {
return (SkeletonKeyToken) super.issuedAt(issuedAt);
public AccessToken issuer(String issuer) {
return (AccessToken) super.issuer(issuer);
}
@Override
public SkeletonKeyToken issuer(String issuer) {
return (SkeletonKeyToken) super.issuer(issuer);
public AccessToken audience(String audience) {
return (AccessToken) super.audience(audience);
}
@Override
public SkeletonKeyToken audience(String audience) {
return (SkeletonKeyToken) super.audience(audience);
public AccessToken subject(String subject) {
return (AccessToken) super.subject(subject);
}
@Override
public SkeletonKeyToken principal(String principal) {
return (SkeletonKeyToken) super.principal(principal);
}
@Override
public SkeletonKeyToken type(String type) {
return (SkeletonKeyToken) super.type(type);
public AccessToken type(String type) {
return (AccessToken) super.type(type);
}
public Set<String> getAllowedOrigins() {
@@ -169,17 +187,10 @@ public class SkeletonKeyToken extends JsonWebToken {
this.trustedCertificates = trustedCertificates;
}
/**
* OAuth client the token was issued for.
*
* @return
*/
public String getIssuedFor() {
return issuedFor;
@Override
public AccessToken issuedFor(String issuedFor) {
return (AccessToken)super.issuedFor(issuedFor);
}
public SkeletonKeyToken issuedFor(String issuedFor) {
this.issuedFor = issuedFor;
return this;
}
}
@@ -21,6 +21,12 @@ public class AccessTokenResponse {
@JsonProperty("token_type")
protected String tokenType;
@JsonProperty("id_token")
protected String idToken;
@JsonProperty("not-before-policy")
protected int notBeforePolicy;
public String getToken() {
return token;
}
@@ -52,4 +58,20 @@ public class AccessTokenResponse {
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getIdToken() {
return idToken;
}
public void setIdToken(String idToken) {
this.idToken = idToken;
}
public int getNotBeforePolicy() {
return notBeforePolicy;
}
public void setNotBeforePolicy(int notBeforePolicy) {
this.notBeforePolicy = notBeforePolicy;
}
}
@@ -0,0 +1,306 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IDToken extends JsonWebToken {
@JsonProperty("nonce")
protected String nonce;
@JsonProperty("name")
protected String name;
@JsonProperty("given_name")
protected String givenName;
@JsonProperty("family_name")
protected String familyName;
@JsonProperty("middle_name")
protected String middleName;
@JsonProperty("nickname")
protected String nickName;
@JsonProperty("preferred_username")
protected String preferredUsername;
@JsonProperty("profile")
protected String profile;
@JsonProperty("picture")
protected String picture;
@JsonProperty("website")
protected String website;
@JsonProperty("email")
protected String email;
@JsonProperty("email_verified")
protected Boolean emailVerified;
@JsonProperty("gender")
protected String gender;
@JsonProperty("birthdate")
protected String birthdate;
@JsonProperty("zoneinfo")
protected String zoneinfo;
@JsonProperty("locale")
protected String locale;
@JsonProperty("phone_number")
protected String phoneNumber;
@JsonProperty("phone_number_verified")
protected Boolean phoneNumberVerified;
@JsonProperty("address")
protected String address;
@JsonProperty("updated_at")
protected Long updatedAt;
@JsonProperty("formatted")
protected String formattedAddress;
@JsonProperty("street_address")
protected String streetAddress;
@JsonProperty("locality")
protected String locality;
@JsonProperty("region")
protected String region;
@JsonProperty("postal_code")
protected String postalCode;
@JsonProperty("country")
protected String country;
@JsonProperty("claims_locales")
protected String claimsLocales;
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGivenName() {
return givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPreferredUsername() {
return preferredUsername;
}
public void setPreferredUsername(String preferredUsername) {
this.preferredUsername = preferredUsername;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getEmailVerified() {
return emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthdate() {
return birthdate;
}
public void setBirthdate(String birthdate) {
this.birthdate = birthdate;
}
public String getZoneinfo() {
return zoneinfo;
}
public void setZoneinfo(String zoneinfo) {
this.zoneinfo = zoneinfo;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Boolean getPhoneNumberVerified() {
return phoneNumberVerified;
}
public void setPhoneNumberVerified(Boolean phoneNumberVerified) {
this.phoneNumberVerified = phoneNumberVerified;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Long getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Long updatedAt) {
this.updatedAt = updatedAt;
}
public String getFormattedAddress() {
return formattedAddress;
}
public void setFormattedAddress(String formattedAddress) {
this.formattedAddress = formattedAddress;
}
public String getStreetAddress() {
return streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
public String getLocality() {
return locality;
}
public void setLocality(String locality) {
this.locality = locality;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getClaimsLocales() {
return claimsLocales;
}
public void setClaimsLocales(String claimsLocales) {
this.claimsLocales = claimsLocales;
}
}
@@ -17,7 +17,7 @@ public class JsonWebToken implements Serializable {
@JsonProperty("nbf")
protected long notBefore;
@JsonProperty("iat")
protected long issuedAt;
protected int issuedAt;
@JsonProperty("iss")
protected String issuer;
@JsonProperty("aud")
@@ -26,6 +26,8 @@ public class JsonWebToken implements Serializable {
protected String subject;
@JsonProperty("typ")
protected String type;
@JsonProperty("azp")
public String issuedFor;
public String getId() {
return id;
@@ -78,7 +80,7 @@ public class JsonWebToken implements Serializable {
return (!isExpired() || expiration == 0) && (isNotBefore() || notBefore == 0);
}
public long getIssuedAt() {
public int getIssuedAt() {
return issuedAt;
}
@@ -87,11 +89,11 @@ public class JsonWebToken implements Serializable {
*/
@JsonIgnore
public JsonWebToken issuedNow() {
issuedAt = System.currentTimeMillis() / 1000;
issuedAt = (int)(System.currentTimeMillis() / 1000);
return this;
}
public JsonWebToken issuedAt(long issuedAt) {
public JsonWebToken issuedAt(int issuedAt) {
this.issuedAt = issuedAt;
return this;
}
@@ -120,8 +122,8 @@ public class JsonWebToken implements Serializable {
return subject;
}
public JsonWebToken principal(String principal) {
this.subject = principal;
public JsonWebToken subject(String subject) {
this.subject = subject;
return this;
}
@@ -133,4 +135,18 @@ public class JsonWebToken implements Serializable {
this.type = type;
return this;
}
/**
* OAuth client the token was issued for.
*
* @return
*/
public String getIssuedFor() {
return issuedFor;
}
public JsonWebToken issuedFor(String issuedFor) {
this.issuedFor = issuedFor;
return this;
}
}
@@ -0,0 +1,36 @@
package org.keycloak.representations;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RefreshToken extends AccessToken {
public RefreshToken() {
type("REFRESH");
}
/**
* Deep copies issuer, subject, issuedFor, realmAccess, and resourceAccess
* from AccessToken.
*
* @param token
*/
public RefreshToken(AccessToken token) {
this();
this.issuer = token.issuer;
this.subject = token.subject;
this.issuedFor = token.issuedFor;
if (token.realmAccess != null) {
realmAccess = token.realmAccess.clone();
}
if (token.resourceAccess != null) {
resourceAccess = new HashMap<String, Access>();
for (Map.Entry<String, Access> entry : token.resourceAccess.entrySet()) {
resourceAccess.put(entry.getKey(), entry.getValue().clone());
}
}
}
}
@@ -1,13 +0,0 @@
package org.keycloak.representations;
import org.keycloak.util.MultivaluedHashMap;
/**
* Key is resource desired. Values are roles desired for that resource
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SkeletonKeyScope extends MultivaluedHashMap<String, String> {
}
@@ -8,18 +8,20 @@ import org.codehaus.jackson.annotate.JsonIgnore;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AdminAction {
public abstract class AdminAction {
protected String id;
protected long expiration;
protected int expiration;
protected String resource;
protected String action;
public AdminAction() {
}
public AdminAction(String id, long expiration, String resource) {
public AdminAction(String id, int expiration, String resource, String action) {
this.id = id;
this.expiration = expiration;
this.resource = resource;
this.action = action;
}
public String getId() {
@@ -36,11 +38,16 @@ public class AdminAction {
return time > expiration;
}
public long getExpiration() {
/**
* Time in seconds since epoc
*
* @return
*/
public int getExpiration() {
return expiration;
}
public void setExpiration(long expiration) {
public void setExpiration(int expiration) {
this.expiration = expiration;
}
@@ -51,4 +58,14 @@ public class AdminAction {
public void setResource(String resource) {
this.resource = resource;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public abstract boolean validate();
}
@@ -5,13 +5,15 @@ package org.keycloak.representations.adapters.action;
* @version $Revision: 1 $
*/
public class LogoutAction extends AdminAction {
public static final String LOGOUT = "LOGOUT";
protected String user;
protected int notBefore;
public LogoutAction() {
}
public LogoutAction(String id, long expiration, String resource, String user) {
super(id, expiration, resource);
public LogoutAction(String id, int expiration, String resource, String user, int notBefore) {
super(id, expiration, resource, LOGOUT);
this.user = user;
}
@@ -22,4 +24,17 @@ public class LogoutAction extends AdminAction {
public void setUser(String user) {
this.user = user;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
@Override
public boolean validate() {
return LOGOUT.equals(action);
}
}
@@ -0,0 +1,33 @@
package org.keycloak.representations.adapters.action;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PushNotBeforeAction extends AdminAction {
public static final String PUSH_NOT_BEFORE = "PUSH_NOT_BEFORE";
protected int notBefore;
public PushNotBeforeAction() {
}
public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
super(id, expiration, resource, PUSH_NOT_BEFORE);
this.notBefore = notBefore;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
@Override
public boolean validate() {
return PUSH_NOT_BEFORE.equals(action);
}
}
@@ -0,0 +1,39 @@
package org.keycloak.representations.adapters.action;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SessionStats {
protected int activeSessions;
protected int activeUsers;
protected Map<String, UserStats> users;
public int getActiveSessions() {
return activeSessions;
}
public void setActiveSessions(int activeSessions) {
this.activeSessions = activeSessions;
}
public int getActiveUsers() {
return activeUsers;
}
public void setActiveUsers(int activeUsers) {
this.activeUsers = activeUsers;
}
public Map<String, UserStats> getUsers() {
return users;
}
public void setUsers(Map<String, UserStats> users) {
this.users = users;
}
}
@@ -0,0 +1,35 @@
package org.keycloak.representations.adapters.action;
/**
* Query session stats.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SessionStatsAction extends AdminAction {
public static final String SESSION_STATS = "SESSION_STATS";
protected boolean listUsers;
public SessionStatsAction() {
}
public SessionStatsAction(String id, int expiration, String resource) {
super(id, expiration, resource, SESSION_STATS);
}
public boolean isListUsers() {
return listUsers;
}
public void setListUsers(boolean listUsers) {
this.listUsers = listUsers;
}
@Override
public boolean validate() {
return SESSION_STATS.equals(action);
}
}
@@ -0,0 +1,26 @@
package org.keycloak.representations.adapters.action;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserStats {
protected boolean loggedIn;
protected long whenLoggedIn;
public boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
public long getWhenLoggedIn() {
return whenLoggedIn;
}
public void setWhenLoggedIn(long whenLoggedIn) {
this.whenLoggedIn = whenLoggedIn;
}
}
@@ -0,0 +1,31 @@
package org.keycloak.representations.adapters.action;
/**
* Query session stats.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserStatsAction extends AdminAction {
public static final String USER_STATS = "USER_STATS";
protected String user;
public UserStatsAction() {
}
public UserStatsAction(String id, int expiration, String resource, String user) {
super(id, expiration, resource, USER_STATS);
this.user = user;
}
public String getUser() {
return user;
}
@Override
public boolean validate() {
return USER_STATS.equals(action);
}
}
@@ -13,7 +13,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
"resource", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only", "scope",
"expose-token", "bearer-only",
"connection-pool-size",
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
"client-keystore", "client-keystore-password", "client-key-password"
@@ -2,7 +2,6 @@ package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.keycloak.representations.SkeletonKeyScope;
import java.util.HashMap;
import java.util.Map;
@@ -14,10 +13,10 @@ import java.util.Map;
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
"resource", "credentials",
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only", "scope"})
"expose-token", "bearer-only"})
public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@@ -35,10 +34,10 @@ public class BaseAdapterConfig extends BaseRealmConfig {
protected boolean exposeToken;
@JsonProperty("bearer-only")
protected boolean bearerOnly;
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
protected Map<String, String> credentials = new HashMap<String, String>();
@JsonProperty("scope")
protected SkeletonKeyScope scope;
public boolean isUseResourceRoleMappings() {
@@ -113,11 +112,11 @@ public class BaseAdapterConfig extends BaseRealmConfig {
this.credentials = credentials;
}
public SkeletonKeyScope getScope() {
return scope;
public boolean isPublicClient() {
return publicClient;
}
public void setScope(SkeletonKeyScope scope) {
this.scope = scope;
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
}
@@ -12,12 +12,17 @@ public class ApplicationRepresentation {
protected String name;
protected String adminUrl;
protected String baseUrl;
protected boolean surrogateAuthRequired;
protected boolean enabled;
protected List<CredentialRepresentation> credentials;
protected Boolean surrogateAuthRequired;
protected Boolean enabled;
protected String secret;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean bearerOnly;
protected Boolean publicClient;
public String getId() {
return id;
@@ -35,19 +40,19 @@ public class ApplicationRepresentation {
this.name = name;
}
public boolean isEnabled() {
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public boolean isSurrogateAuthRequired() {
public Boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
@@ -67,21 +72,12 @@ public class ApplicationRepresentation {
this.baseUrl = baseUrl;
}
public List<CredentialRepresentation> getCredentials() {
return credentials;
public String getSecret() {
return secret;
}
public void setCredentials(List<CredentialRepresentation> credentials) {
this.credentials = credentials;
}
public ApplicationRepresentation credential(String type, String value) {
if (this.credentials == null) credentials = new ArrayList<CredentialRepresentation>();
CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(type);
cred.setValue(value);
credentials.add(cred);
return this;
public void setSecret(String secret) {
this.secret = secret;
}
public List<String> getRedirectUris() {
@@ -107,4 +103,36 @@ public class ApplicationRepresentation {
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
public ClaimRepresentation getClaims() {
return claims;
}
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(Boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
}
@@ -0,0 +1,98 @@
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClaimRepresentation {
protected boolean name;
protected boolean username;
protected boolean profile;
protected boolean picture;
protected boolean website;
protected boolean email;
protected boolean gender;
protected boolean locale;
protected boolean address;
protected boolean phone;
public boolean getName() {
return name;
}
public void setName(boolean name) {
this.name = name;
}
public boolean getUsername() {
return username;
}
public void setUsername(boolean username) {
this.username = username;
}
public boolean getProfile() {
return profile;
}
public void setProfile(boolean profile) {
this.profile = profile;
}
public boolean getPicture() {
return picture;
}
public void setPicture(boolean picture) {
this.picture = picture;
}
public boolean getWebsite() {
return website;
}
public void setWebsite(boolean website) {
this.website = website;
}
public boolean getEmail() {
return email;
}
public void setEmail(boolean email) {
this.email = email;
}
public boolean getGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public boolean getLocale() {
return locale;
}
public void setLocale(boolean locale) {
this.locale = locale;
}
public boolean getAddress() {
return address;
}
public void setAddress(boolean address) {
this.address = address;
}
public boolean getPhone() {
return phone;
}
public void setPhone(boolean phone) {
this.phone = phone;
}
}
@@ -12,8 +12,12 @@ public class OAuthClientRepresentation {
protected String baseUrl;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected boolean enabled;
protected List<CredentialRepresentation> credentials;
protected Boolean enabled;
protected String secret;
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean publicClient;
public String getId() {
return id;
@@ -31,11 +35,11 @@ public class OAuthClientRepresentation {
this.name = name;
}
public boolean isEnabled() {
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@@ -63,11 +67,35 @@ public class OAuthClientRepresentation {
this.webOrigins = webOrigins;
}
public List<CredentialRepresentation> getCredentials() {
return credentials;
public String getSecret() {
return secret;
}
public void setCredentials(List<CredentialRepresentation> credentials) {
this.credentials = credentials;
public void setSecret(String secret) {
this.secret = secret;
}
public ClaimRepresentation getClaims() {
return claims;
}
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
}
@@ -15,37 +15,25 @@ import java.security.PublicKey;
*/
public class PublishedRealmRepresentation {
protected String realm;
protected String self;
@JsonProperty("public_key")
protected String publicKeyPem;
@JsonProperty("authorization")
protected String authorizationUrl;
@JsonProperty("token-service")
protected String tokenServiceUrl;
@JsonProperty("codes")
protected String codeUrl;
@JsonProperty("account-service")
protected String accountServiceUrl;
@JsonProperty("grants")
protected String grantUrl;
@JsonProperty("admin-api")
protected String adminApiUrl;
@JsonProperty("identity-grants")
protected String identityGrantUrl;
@JsonProperty("admin-role")
protected String adminRole;
@JsonProperty("tokens-not-before")
protected int notBefore;
@JsonIgnore
protected volatile transient PublicKey publicKey;
public String getAdminRole() {
return adminRole;
}
public void setAdminRole(String adminRole) {
this.adminRole = adminRole;
}
public String getRealm() {
return realm;
}
@@ -54,14 +42,6 @@ public class PublishedRealmRepresentation {
this.realm = realm;
}
public String getSelf() {
return self;
}
public void setSelf(String self) {
this.self = self;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
@@ -100,36 +80,35 @@ public class PublishedRealmRepresentation {
this.publicKeyPem = PemUtils.removeBeginEnd(s);
}
public String getAuthorizationUrl() {
return authorizationUrl;
public String getTokenServiceUrl() {
return tokenServiceUrl;
}
public void setAuthorizationUrl(String authorizationUrl) {
this.authorizationUrl = authorizationUrl;
public void setTokenServiceUrl(String tokenServiceUrl) {
this.tokenServiceUrl = tokenServiceUrl;
}
public String getCodeUrl() {
return codeUrl;
public String getAccountServiceUrl() {
return accountServiceUrl;
}
public void setCodeUrl(String codeUrl) {
this.codeUrl = codeUrl;
public void setAccountServiceUrl(String accountServiceUrl) {
this.accountServiceUrl = accountServiceUrl;
}
public String getGrantUrl() {
return grantUrl;
public String getAdminApiUrl() {
return adminApiUrl;
}
public void setGrantUrl(String grantUrl) {
this.grantUrl = grantUrl;
public void setAdminApiUrl(String adminApiUrl) {
this.adminApiUrl = adminApiUrl;
}
public String getIdentityGrantUrl() {
return identityGrantUrl;
public int getNotBefore() {
return notBefore;
}
public void setIdentityGrantUrl(String identityGrantUrl) {
this.identityGrantUrl = identityGrantUrl;
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
}
@@ -13,12 +13,16 @@ public class RealmRepresentation {
protected String self; // link
protected String id;
protected String realm;
protected Integer tokenLifespan;
protected Integer notBefore;
protected Integer accessTokenLifespan;
protected Integer refreshTokenLifespan;
protected Integer centralLoginLifespan;
protected Integer accessCodeLifespan;
protected Integer accessCodeLifespanUserAction;
protected Boolean enabled;
protected Boolean sslNotRequired;
protected Boolean registrationAllowed;
protected Boolean rememberMe;
protected Boolean verifyEmail;
protected Boolean resetPasswordAllowed;
protected Boolean social;
@@ -114,12 +118,28 @@ public class RealmRepresentation {
this.sslNotRequired = sslNotRequired;
}
public Integer getTokenLifespan() {
return tokenLifespan;
public Integer getAccessTokenLifespan() {
return accessTokenLifespan;
}
public void setTokenLifespan(Integer tokenLifespan) {
this.tokenLifespan = tokenLifespan;
public void setAccessTokenLifespan(Integer accessTokenLifespan) {
this.accessTokenLifespan = accessTokenLifespan;
}
public Integer getRefreshTokenLifespan() {
return refreshTokenLifespan;
}
public Integer getCentralLoginLifespan() {
return centralLoginLifespan;
}
public void setCentralLoginLifespan(Integer centralLoginLifespan) {
this.centralLoginLifespan = centralLoginLifespan;
}
public void setRefreshTokenLifespan(Integer refreshTokenLifespan) {
this.refreshTokenLifespan = refreshTokenLifespan;
}
public List<UserRoleMappingRepresentation> getRoleMappings() {
@@ -140,7 +160,7 @@ public class RealmRepresentation {
public ScopeMappingRepresentation scopeMapping(String username) {
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
mapping.setUsername(username);
mapping.setClient(username);
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
scopeMappings.add(mapping);
return mapping;
@@ -222,6 +242,14 @@ public class RealmRepresentation {
this.registrationAllowed = registrationAllowed;
}
public Boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(Boolean rememberMe) {
this.rememberMe = rememberMe;
}
public Boolean isVerifyEmail() {
return verifyEmail;
}
@@ -317,4 +345,12 @@ public class RealmRepresentation {
public void setAccountTheme(String accountTheme) {
this.accountTheme = accountTheme;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
}
@@ -9,7 +9,7 @@ import java.util.Set;
*/
public class ScopeMappingRepresentation {
protected String self; // link
protected String username;
protected String client;
protected Set<String> roles;
public String getSelf() {
@@ -20,12 +20,12 @@ public class ScopeMappingRepresentation {
this.self = self;
}
public String getUsername() {
return username;
public String getClient() {
return client;
}
public void setUsername(String username) {
this.username = username;
public void setClient(String client) {
this.client = client;
}
public Set<String> getRoles() {
@@ -6,6 +6,7 @@ package org.keycloak.representations.idm;
public class SocialLinkRepresentation {
protected String socialProvider;
protected String socialUserId;
protected String socialUsername;
public String getSocialProvider() {
@@ -16,6 +17,14 @@ public class SocialLinkRepresentation {
this.socialProvider = socialProvider;
}
public String getSocialUserId() {
return socialUserId;
}
public void setSocialUserId(String socialUserId) {
this.socialUserId = socialUserId;
}
public String getSocialUsername() {
return socialUsername;
}
@@ -32,9 +32,10 @@ public class SocialMappingRepresentation {
return socialLinks;
}
public SocialLinkRepresentation socialLink(String socialProvider, String socialUsername) {
public SocialLinkRepresentation socialLink(String socialProvider, String socialUserId, String socialUsername) {
SocialLinkRepresentation link = new SocialLinkRepresentation();
link.setSocialProvider(socialProvider);
link.setSocialUserId(socialUserId);
link.setSocialUsername(socialUsername);
if (socialLinks == null) socialLinks = new ArrayList<SocialLinkRepresentation>();
socialLinks.add(link);
@@ -12,6 +12,7 @@ import java.util.Map;
public class UserRepresentation {
protected String self; // link
protected String id;
protected String username;
protected boolean enabled;
protected boolean totp;
@@ -31,6 +32,14 @@ public class UserRepresentation {
this.self = self;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
+44
View File
@@ -0,0 +1,44 @@
package org.keycloak.util;
import net.iharder.Base64;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class BasicAuthHelper
{
public static String createHeader(String username, String password)
{
StringBuffer buf = new StringBuffer(username);
buf.append(':').append(password);
try
{
return "Basic " + Base64.encodeBytes(buf.toString().getBytes("UTF-8"));
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
public static String[] parseHeader(String header)
{
if (header.length() < 6) return null;
String type = header.substring(0, 5);
type = type.toLowerCase();
if (!type.equalsIgnoreCase("Basic")) return null;
String val = header.substring(6);
try {
val = new String(Base64.decode(val.getBytes()));
} catch (IOException e) {
throw new RuntimeException(e);
}
String[] split = val.split(":");
if (split.length != 2) return null;
return split;
}
}
@@ -6,6 +6,7 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Utility class to handle simple JSON serializable for Keycloak.
@@ -18,13 +19,16 @@ public class JsonSerialization {
public static final ObjectMapper prettyMapper = new ObjectMapper();
static {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
prettyMapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
prettyMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
prettyMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
mapper.writeValue(os, obj);
}
public static String writeValueAsString(Object obj) throws IOException {
return mapper.writeValueAsString(obj);
}
Loaded 100 of 480 files, more files were not shown because too many files have changed in this diff. Show more