keycloakify 9.0.0-rc.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +8 -18
  2. package/account/Template.js +1 -1
  3. package/account/Template.js.map +1 -1
  4. package/account/TemplateProps.d.ts +1 -1
  5. package/account/kcContext/KcContext.d.ts +1 -0
  6. package/account/kcContext/KcContext.js.map +1 -1
  7. package/account/kcContext/kcContextMocks.js +1 -0
  8. package/account/kcContext/kcContextMocks.js.map +1 -1
  9. package/account/lib/useGetClassName.js +1 -0
  10. package/account/lib/useGetClassName.js.map +1 -1
  11. package/account/pages/Account.js +1 -1
  12. package/account/pages/Account.js.map +1 -1
  13. package/bin/getSrcDirPath.js +30 -17
  14. package/bin/getSrcDirPath.js.map +1 -1
  15. package/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +6 -0
  16. package/bin/keycloakify/generateFtl/generateFtl.js +22 -0
  17. package/bin/keycloakify/generateFtl/generateFtl.js.map +1 -1
  18. package/bin/keycloakify/generateFtl/pageId.d.ts +1 -1
  19. package/bin/keycloakify/generateFtl/pageId.js +2 -0
  20. package/bin/keycloakify/generateFtl/pageId.js.map +1 -1
  21. package/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.js +1 -5
  22. package/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.js.map +1 -1
  23. package/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.js +32 -103
  24. package/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.js.map +1 -1
  25. package/bin/keycloakify/generateStartKeycloakTestingContainer.d.ts +1 -0
  26. package/bin/keycloakify/generateStartKeycloakTestingContainer.js +4 -3
  27. package/bin/keycloakify/generateStartKeycloakTestingContainer.js.map +1 -1
  28. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.d.ts +0 -1
  29. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.js +22 -13
  30. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.js.map +1 -1
  31. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.d.ts +0 -2
  32. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.js +37 -51
  33. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.js.map +1 -1
  34. package/bin/keycloakify/keycloakify.js +3 -2
  35. package/bin/keycloakify/keycloakify.js.map +1 -1
  36. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.js +7 -3
  37. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.js.map +1 -1
  38. package/lib/usePrepareTemplate.js +7 -8
  39. package/lib/usePrepareTemplate.js.map +1 -1
  40. package/login/Fallback.js +6 -0
  41. package/login/Fallback.js.map +1 -1
  42. package/login/Template.js +1 -1
  43. package/login/Template.js.map +1 -1
  44. package/login/TemplateProps.d.ts +1 -1
  45. package/login/kcContext/KcContext.d.ts +21 -1
  46. package/login/kcContext/KcContext.js.map +1 -1
  47. package/login/kcContext/kcContextMocks.d.ts +1 -1
  48. package/login/kcContext/kcContextMocks.js +12 -1
  49. package/login/kcContext/kcContextMocks.js.map +1 -1
  50. package/login/lib/useGetClassName.js +2 -0
  51. package/login/lib/useGetClassName.js.map +1 -1
  52. package/login/pages/Info.js +1 -1
  53. package/login/pages/Info.js.map +1 -1
  54. package/login/pages/LoginConfigTotp.js +1 -1
  55. package/login/pages/LoginConfigTotp.js.map +1 -1
  56. package/login/pages/LoginDeviceVerifyUserCode.d.ts +7 -0
  57. package/login/pages/LoginDeviceVerifyUserCode.js +15 -0
  58. package/login/pages/LoginDeviceVerifyUserCode.js.map +1 -0
  59. package/login/pages/LoginOauthGrant.d.ts +7 -0
  60. package/login/pages/LoginOauthGrant.js +15 -0
  61. package/login/pages/LoginOauthGrant.js.map +1 -0
  62. package/package.json +11 -67
  63. package/src/account/Template.tsx +1 -1
  64. package/src/account/TemplateProps.ts +1 -1
  65. package/src/account/kcContext/KcContext.ts +1 -0
  66. package/src/account/kcContext/kcContextMocks.ts +1 -0
  67. package/src/account/lib/useGetClassName.ts +1 -0
  68. package/src/account/pages/Account.tsx +4 -4
  69. package/src/bin/getSrcDirPath.ts +8 -8
  70. package/src/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +6 -0
  71. package/src/bin/keycloakify/generateFtl/generateFtl.ts +21 -0
  72. package/src/bin/keycloakify/generateFtl/pageId.ts +2 -0
  73. package/src/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.ts +1 -6
  74. package/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts +32 -103
  75. package/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +11 -4
  76. package/src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts +29 -15
  77. package/src/bin/keycloakify/generateTheme/readStaticResourcesUsage.ts +21 -28
  78. package/src/bin/keycloakify/keycloakify.ts +3 -3
  79. package/src/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.ts +14 -7
  80. package/src/lib/usePrepareTemplate.ts +7 -10
  81. package/src/login/Fallback.tsx +6 -0
  82. package/src/login/Template.tsx +1 -1
  83. package/src/login/TemplateProps.ts +3 -1
  84. package/src/login/kcContext/KcContext.ts +24 -0
  85. package/src/login/kcContext/kcContextMocks.ts +23 -1
  86. package/src/login/lib/useGetClassName.ts +2 -0
  87. package/src/login/pages/Info.tsx +4 -1
  88. package/src/login/pages/LoginConfigTotp.tsx +1 -1
  89. package/src/login/pages/LoginDeviceVerifyUserCode.tsx +68 -0
  90. package/src/login/pages/LoginOauthGrant.tsx +73 -0
  91. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountPages.java +0 -33
  92. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProvider.java +0 -76
  93. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProviderFactory.java +0 -25
  94. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountSpi.java +0 -50
  95. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProvider.java +0 -424
  96. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProviderFactory.java +0 -51
  97. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/Templates.java +0 -51
  98. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountBean.java +0 -91
  99. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountFederatedIdentityBean.java +0 -157
  100. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ApplicationsBean.java +0 -258
  101. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AuthorizationBean.java +0 -515
  102. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/FeaturesBean.java +0 -56
  103. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/LogBean.java +0 -95
  104. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/PasswordBean.java +0 -34
  105. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/RealmBean.java +0 -75
  106. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ReferrerBean.java +0 -38
  107. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/SessionsBean.java +0 -93
  108. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/TotpBean.java +0 -125
  109. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/UrlBean.java +0 -121
  110. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/AccountUrls.java +0 -115
  111. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormService.java +0 -1320
  112. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormServiceFactory.java +0 -64
  113. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountPages.java +0 -33
  114. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProvider.java +0 -76
  115. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProviderFactory.java +0 -25
  116. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountSpi.java +0 -50
  117. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProvider.java +0 -424
  118. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProviderFactory.java +0 -51
  119. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/Templates.java +0 -51
  120. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountBean.java +0 -91
  121. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountFederatedIdentityBean.java +0 -157
  122. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ApplicationsBean.java +0 -258
  123. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AuthorizationBean.java +0 -515
  124. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/FeaturesBean.java +0 -56
  125. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/LogBean.java +0 -95
  126. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/PasswordBean.java +0 -34
  127. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/RealmBean.java +0 -75
  128. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ReferrerBean.java +0 -38
  129. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/SessionsBean.java +0 -93
  130. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/TotpBean.java +0 -125
  131. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/UrlBean.java +0 -121
  132. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/AccountUrls.java +0 -115
  133. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormService.java +0 -1320
  134. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormServiceFactory.java +0 -64
@@ -1,1320 +0,0 @@
1
- /*
2
- * Copyright 2022 Red Hat, Inc. and/or its affiliates
3
- * and other contributors as indicated by the @author tags.
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- package org.keycloak.services.resources.account;
18
-
19
- import com.google.common.base.Strings;
20
- import jakarta.ws.rs.Consumes;
21
- import jakarta.ws.rs.FormParam;
22
- import jakarta.ws.rs.GET;
23
- import jakarta.ws.rs.NotFoundException;
24
- import jakarta.ws.rs.POST;
25
- import jakarta.ws.rs.Path;
26
- import jakarta.ws.rs.PathParam;
27
- import jakarta.ws.rs.Produces;
28
- import jakarta.ws.rs.QueryParam;
29
- import jakarta.ws.rs.core.MediaType;
30
- import jakarta.ws.rs.core.MultivaluedMap;
31
- import jakarta.ws.rs.core.Response;
32
- import jakarta.ws.rs.core.Response.Status;
33
- import jakarta.ws.rs.core.UriBuilder;
34
- import jakarta.ws.rs.core.UriInfo;
35
- import java.io.IOException;
36
- import java.lang.reflect.Method;
37
- import java.net.URI;
38
- import java.nio.charset.StandardCharsets;
39
- import java.security.MessageDigest;
40
- import java.util.ArrayList;
41
- import java.util.Arrays;
42
- import java.util.EnumMap;
43
- import java.util.HashSet;
44
- import java.util.Iterator;
45
- import java.util.List;
46
- import java.util.Map;
47
- import java.util.Objects;
48
- import java.util.Set;
49
- import java.util.UUID;
50
- import java.util.stream.Collectors;
51
- import lombok.extern.jbosslog.JBossLog;
52
- import org.jboss.logging.Logger;
53
- import org.keycloak.authorization.AuthorizationProvider;
54
- import org.keycloak.authorization.model.PermissionTicket;
55
- import org.keycloak.authorization.model.Policy;
56
- import org.keycloak.authorization.model.Resource;
57
- import org.keycloak.authorization.model.ResourceServer;
58
- import org.keycloak.authorization.model.Scope;
59
- import org.keycloak.authorization.store.PermissionTicketStore;
60
- import org.keycloak.authorization.store.PolicyStore;
61
- import org.keycloak.authorization.store.ScopeStore;
62
- import org.keycloak.common.Profile;
63
- import org.keycloak.common.util.Base64Url;
64
- import org.keycloak.common.util.Time;
65
- import org.keycloak.common.util.UriUtils;
66
- import org.keycloak.events.Details;
67
- import org.keycloak.events.Errors;
68
- import org.keycloak.events.Event;
69
- import org.keycloak.events.EventBuilder;
70
- import org.keycloak.events.EventStoreProvider;
71
- import org.keycloak.events.EventType;
72
- import org.keycloak.forms.account.AccountPages;
73
- import org.keycloak.forms.account.AccountProvider;
74
- import org.keycloak.forms.login.LoginFormsProvider;
75
- import org.keycloak.locale.LocaleSelectorProvider;
76
- import org.keycloak.locale.LocaleUpdaterProvider;
77
- import org.keycloak.models.AccountRoles;
78
- import org.keycloak.models.AuthenticatedClientSessionModel;
79
- import org.keycloak.models.ClientModel;
80
- import org.keycloak.models.ClientSessionContext;
81
- import org.keycloak.models.FederatedIdentityModel;
82
- import org.keycloak.models.KeycloakSession;
83
- import org.keycloak.models.ModelException;
84
- import org.keycloak.models.OTPPolicy;
85
- import org.keycloak.models.RealmModel;
86
- import org.keycloak.models.UserCredentialModel;
87
- import org.keycloak.models.UserModel;
88
- import org.keycloak.models.UserSessionModel;
89
- import org.keycloak.models.credential.OTPCredentialModel;
90
- import org.keycloak.models.credential.PasswordCredentialModel;
91
- import org.keycloak.models.utils.CredentialValidation;
92
- import org.keycloak.models.utils.FormMessage;
93
- import org.keycloak.protocol.oidc.TokenManager;
94
- import org.keycloak.protocol.oidc.utils.RedirectUtils;
95
- import org.keycloak.representations.IDToken;
96
- import org.keycloak.services.AccountUrls;
97
- import org.keycloak.services.ErrorResponse;
98
- import org.keycloak.services.ForbiddenException;
99
- import org.keycloak.services.ServicesLogger;
100
- import org.keycloak.services.managers.AppAuthManager;
101
- import org.keycloak.services.managers.Auth;
102
- import org.keycloak.services.managers.AuthenticationManager;
103
- import org.keycloak.services.managers.AuthenticationSessionManager;
104
- import org.keycloak.services.managers.UserConsentManager;
105
- import org.keycloak.services.messages.Messages;
106
- import org.keycloak.services.resource.AccountResourceProvider;
107
- import org.keycloak.services.resources.AbstractSecuredLocalService;
108
- import org.keycloak.services.resources.RealmsResource;
109
- import org.keycloak.services.util.DefaultClientSessionContext;
110
- import org.keycloak.services.util.ResolveRelative;
111
- import org.keycloak.services.validation.Validation;
112
- import org.keycloak.sessions.AuthenticationSessionModel;
113
- import org.keycloak.storage.ReadOnlyException;
114
- import org.keycloak.theme.Theme;
115
- import org.keycloak.userprofile.EventAuditingAttributeChangeListener;
116
- import org.keycloak.userprofile.UserProfile;
117
- import org.keycloak.userprofile.UserProfileContext;
118
- import org.keycloak.userprofile.UserProfileProvider;
119
- import org.keycloak.userprofile.ValidationException;
120
- import org.keycloak.util.JsonSerialization;
121
- import org.keycloak.utils.CredentialHelper;
122
-
123
- /**
124
- * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
125
- */
126
- @JBossLog
127
- public class AccountFormService extends AbstractSecuredLocalService
128
- implements AccountResourceProvider {
129
-
130
- public static final String THEME_NAME = "account-v1";
131
-
132
- @Override
133
- public boolean useWithTheme(Theme theme) {
134
- log.infof("Attempt to use with theme %s", theme.getName());
135
- return ((!Strings.isNullOrEmpty(theme.getName()) && THEME_NAME.equals(theme.getName()))
136
- || (!Strings.isNullOrEmpty(theme.getParentName())
137
- && THEME_NAME.equals(theme.getParentName())));
138
- }
139
-
140
- @Override
141
- public Object getResource() {
142
- return this;
143
- }
144
-
145
- @Override
146
- public void close() {}
147
-
148
- private static final Logger logger = Logger.getLogger(AccountFormService.class);
149
-
150
- private static Set<String> VALID_PATHS = new HashSet<>();
151
-
152
- static {
153
- for (Method m : AccountFormService.class.getMethods()) {
154
- Path p = m.getAnnotation(Path.class);
155
- if (p != null) {
156
- VALID_PATHS.add(p.value());
157
- }
158
- }
159
- }
160
-
161
- // Used when some other context (ie. IdentityBrokerService) wants to forward error to account
162
- // management and display it here
163
- public static final String ACCOUNT_MGMT_FORWARDED_ERROR_NOTE = "ACCOUNT_MGMT_FORWARDED_ERROR";
164
-
165
- private final AppAuthManager authManager;
166
- private final EventBuilder event;
167
- private AccountProvider account;
168
- private EventStoreProvider eventStore;
169
-
170
- public AccountFormService(KeycloakSession session, ClientModel client, EventBuilder event) {
171
- super(session, client);
172
- this.event = event;
173
- this.authManager = new AppAuthManager();
174
- init();
175
- }
176
-
177
- public void init() {
178
- log.info("init");
179
- session.getContext().setClient(client);
180
- eventStore = session.getProvider(EventStoreProvider.class);
181
-
182
- account =
183
- session
184
- .getProvider(AccountProvider.class)
185
- .setRealm(realm)
186
- .setUriInfo(session.getContext().getUri())
187
- .setHttpHeaders(headers);
188
-
189
- AuthenticationManager.AuthResult authResult =
190
- authManager.authenticateIdentityCookie(session, realm);
191
- if (authResult != null) {
192
- stateChecker = (String) session.getAttribute("state_checker");
193
- auth =
194
- new Auth(
195
- realm,
196
- authResult.getToken(),
197
- authResult.getUser(),
198
- client,
199
- authResult.getSession(),
200
- true);
201
- account.setStateChecker(stateChecker);
202
- }
203
-
204
- String requestOrigin = UriUtils.getOrigin(session.getContext().getUri().getBaseUri());
205
-
206
- String origin = headers.getRequestHeaders().getFirst("Origin");
207
- if (origin != null && !origin.equals("null") && !requestOrigin.equals(origin)) {
208
- throw new ForbiddenException();
209
- }
210
-
211
- if (!request.getHttpMethod().equals("GET")) {
212
- String referrer = headers.getRequestHeaders().getFirst("Referer");
213
- if (referrer != null && !requestOrigin.equals(UriUtils.getOrigin(referrer))) {
214
- throw new ForbiddenException();
215
- }
216
- }
217
-
218
- if (authResult != null) {
219
- UserSessionModel userSession = authResult.getSession();
220
- if (userSession != null) {
221
- AuthenticatedClientSessionModel clientSession =
222
- userSession.getAuthenticatedClientSessionByClient(client.getId());
223
- if (clientSession == null) {
224
- clientSession =
225
- session.sessions().createClientSession(userSession.getRealm(), client, userSession);
226
- }
227
- auth.setClientSession(clientSession);
228
- }
229
-
230
- account.setUser(auth.getUser());
231
-
232
- ClientSessionContext clientSessionCtx =
233
- DefaultClientSessionContext.fromClientSessionScopeParameter(
234
- auth.getClientSession(), session);
235
- IDToken idToken =
236
- new TokenManager()
237
- .responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
238
- .accessToken(authResult.getToken())
239
- .generateIDToken()
240
- .getIdToken();
241
- idToken.issuedFor(client.getClientId());
242
- account.setIdTokenHint(session.tokens().encodeAndEncrypt(idToken));
243
- }
244
-
245
- account.setFeatures(
246
- realm.isIdentityFederationEnabled(),
247
- eventStore != null && realm.isEventsEnabled(),
248
- true,
249
- Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION));
250
- }
251
-
252
- public static UriBuilder accountServiceBaseUrl(UriInfo uriInfo) {
253
- UriBuilder base =
254
- uriInfo
255
- .getBaseUriBuilder()
256
- .path(RealmsResource.class)
257
- .path(RealmsResource.class, "getAccountService");
258
- return base;
259
- }
260
-
261
- public static UriBuilder accountServiceApplicationPage(UriInfo uriInfo) {
262
- return accountServiceBaseUrl(uriInfo).path(AccountFormService.class, "applicationsPage");
263
- }
264
-
265
- protected Set<String> getValidPaths() {
266
- return AccountFormService.VALID_PATHS;
267
- }
268
-
269
- private Response forwardToPage(String path, AccountPages page) {
270
- if (auth != null) {
271
- try {
272
- auth.require(AccountRoles.MANAGE_ACCOUNT);
273
- } catch (ForbiddenException e) {
274
- return session
275
- .getProvider(LoginFormsProvider.class)
276
- .setError(Messages.NO_ACCESS)
277
- .createErrorPage(Response.Status.FORBIDDEN);
278
- }
279
-
280
- setReferrerOnPage();
281
-
282
- UserSessionModel userSession = auth.getSession();
283
-
284
- String tabId =
285
- session
286
- .getContext()
287
- .getUri()
288
- .getQueryParameters()
289
- .getFirst(org.keycloak.models.Constants.TAB_ID);
290
- if (tabId != null) {
291
- AuthenticationSessionModel authSession =
292
- new AuthenticationSessionManager(session)
293
- .getAuthenticationSessionByIdAndClient(realm, userSession.getId(), client, tabId);
294
- if (authSession != null) {
295
- String forwardedError = authSession.getAuthNote(ACCOUNT_MGMT_FORWARDED_ERROR_NOTE);
296
- if (forwardedError != null) {
297
- try {
298
- FormMessage errorMessage =
299
- JsonSerialization.readValue(forwardedError, FormMessage.class);
300
- account.setError(
301
- Response.Status.INTERNAL_SERVER_ERROR,
302
- errorMessage.getMessage(),
303
- errorMessage.getParameters());
304
- authSession.removeAuthNote(ACCOUNT_MGMT_FORWARDED_ERROR_NOTE);
305
- } catch (IOException ioe) {
306
- throw new RuntimeException(ioe);
307
- }
308
- }
309
- }
310
- }
311
-
312
- String locale =
313
- session
314
- .getContext()
315
- .getUri()
316
- .getQueryParameters()
317
- .getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM);
318
- if (locale != null) {
319
- LocaleUpdaterProvider updater = session.getProvider(LocaleUpdaterProvider.class);
320
- updater.updateUsersLocale(auth.getUser(), locale);
321
- }
322
-
323
- return account.createResponse(page);
324
- } else {
325
- return login(path);
326
- }
327
- }
328
-
329
- private void setReferrerOnPage() {
330
- String[] referrer = getReferrer();
331
- if (referrer != null) {
332
- account.setReferrer(referrer);
333
- }
334
- }
335
-
336
- /**
337
- * Get account information.
338
- *
339
- * @return
340
- */
341
- @Path("/")
342
- @GET
343
- @Produces(MediaType.TEXT_HTML)
344
- public Response accountPage() {
345
- log.info("accountPage");
346
- return forwardToPage(null, AccountPages.ACCOUNT);
347
- }
348
-
349
- public static UriBuilder totpUrl(UriBuilder base) {
350
- return RealmsResource.accountUrl(base).path(AccountFormService.class, "totpPage");
351
- }
352
-
353
- @Path("totp")
354
- @GET
355
- public Response totpPage() {
356
- account.setAttribute(
357
- "mode", session.getContext().getUri().getQueryParameters().getFirst("mode"));
358
- return forwardToPage("totp", AccountPages.TOTP);
359
- }
360
-
361
- public static UriBuilder passwordUrl(UriBuilder base) {
362
- return RealmsResource.accountUrl(base).path(AccountFormService.class, "passwordPage");
363
- }
364
-
365
- @Path("password")
366
- @GET
367
- public Response passwordPage() {
368
- if (auth != null) {
369
- account.setPasswordSet(isPasswordSet(session, realm, auth.getUser()));
370
- }
371
-
372
- return forwardToPage("password", AccountPages.PASSWORD);
373
- }
374
-
375
- @Path("identity")
376
- @GET
377
- public Response federatedIdentityPage() {
378
- return forwardToPage("identity", AccountPages.FEDERATED_IDENTITY);
379
- }
380
-
381
- @Path("log")
382
- @GET
383
- public Response logPage() {
384
- if (!realm.isEventsEnabled()) {
385
- throw new NotFoundException();
386
- }
387
-
388
- if (auth != null) {
389
- List<Event> events =
390
- eventStore
391
- .createQuery()
392
- .type(Constants.EXPOSED_LOG_EVENTS)
393
- .realm(auth.getRealm().getId())
394
- .user(auth.getUser().getId())
395
- .maxResults(30)
396
- .getResultStream()
397
- .peek(
398
- e -> {
399
- if (e.getDetails() != null) {
400
- Iterator<Map.Entry<String, String>> itr =
401
- e.getDetails().entrySet().iterator();
402
- while (itr.hasNext()) {
403
- if (!Constants.EXPOSED_LOG_DETAILS.contains(itr.next().getKey())) {
404
- itr.remove();
405
- }
406
- }
407
- }
408
- })
409
- .collect(Collectors.toList());
410
- account.setEvents(events);
411
- }
412
- return forwardToPage("log", AccountPages.LOG);
413
- }
414
-
415
- @Path("sessions")
416
- @GET
417
- public Response sessionsPage() {
418
- if (auth != null) {
419
- account.setSessions(
420
- session
421
- .sessions()
422
- .getUserSessionsStream(realm, auth.getUser())
423
- .collect(Collectors.toList()));
424
- }
425
- return forwardToPage("sessions", AccountPages.SESSIONS);
426
- }
427
-
428
- @Path("applications")
429
- @GET
430
- public Response applicationsPage() {
431
- return forwardToPage("applications", AccountPages.APPLICATIONS);
432
- }
433
-
434
- /**
435
- * Update account information.
436
- *
437
- * <p>Form params:
438
- *
439
- * <p>firstName lastName email
440
- *
441
- * @return
442
- */
443
- @Path("/")
444
- @POST
445
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
446
- public Response processAccountUpdate() {
447
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
448
-
449
- if (auth == null) {
450
- return login(null);
451
- }
452
-
453
- auth.require(AccountRoles.MANAGE_ACCOUNT);
454
-
455
- String action = formData.getFirst("submitAction");
456
- if (action != null && action.equals("Cancel")) {
457
- setReferrerOnPage();
458
- return account.createResponse(AccountPages.ACCOUNT);
459
- }
460
-
461
- csrfCheck(formData);
462
-
463
- UserModel user = auth.getUser();
464
-
465
- event
466
- .event(EventType.UPDATE_PROFILE)
467
- .client(auth.getClient())
468
- .user(auth.getUser())
469
- .detail(Details.CONTEXT, UserProfileContext.ACCOUNT_OLD.name());
470
-
471
- UserProfileProvider profileProvider = session.getProvider(UserProfileProvider.class);
472
- UserProfile profile = profileProvider.create(UserProfileContext.ACCOUNT_OLD, formData, user);
473
-
474
- try {
475
- // backward compatibility with old account console where attributes are not removed if missing
476
- profile.update(false, new EventAuditingAttributeChangeListener(profile, event));
477
- } catch (ValidationException pve) {
478
- List<FormMessage> errors = Validation.getFormErrorsFromValidation(pve.getErrors());
479
-
480
- if (!errors.isEmpty()) {
481
- setReferrerOnPage();
482
- Response.Status status = Status.OK;
483
-
484
- if (pve.hasError(Messages.READ_ONLY_USERNAME)) {
485
- status = Response.Status.BAD_REQUEST;
486
- } else if (pve.hasError(Messages.EMAIL_EXISTS, Messages.USERNAME_EXISTS)) {
487
- status = Response.Status.CONFLICT;
488
- }
489
-
490
- return account
491
- .setErrors(status, errors)
492
- .setProfileFormData(formData)
493
- .createResponse(AccountPages.ACCOUNT);
494
- }
495
- } catch (ReadOnlyException e) {
496
- setReferrerOnPage();
497
- return account
498
- .setError(Response.Status.BAD_REQUEST, Messages.READ_ONLY_USER)
499
- .setProfileFormData(formData)
500
- .createResponse(AccountPages.ACCOUNT);
501
- }
502
-
503
- event.success();
504
- setReferrerOnPage();
505
- return account.setSuccess(Messages.ACCOUNT_UPDATED).createResponse(AccountPages.ACCOUNT);
506
- }
507
-
508
- @Path("sessions")
509
- @POST
510
- public Response processSessionsLogout() {
511
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
512
-
513
- if (auth == null) {
514
- return login("sessions");
515
- }
516
-
517
- auth.require(AccountRoles.MANAGE_ACCOUNT);
518
- csrfCheck(formData);
519
-
520
- UserModel user = auth.getUser();
521
-
522
- // Rather decrease time a bit. To avoid situation when user is immediatelly redirected to login
523
- // screen, then automatically authenticated (eg. with Kerberos) and then seeing issues due the
524
- // stale token
525
- // as time on the token will be same like notBefore
526
- session.users().setNotBeforeForUser(realm, user, Time.currentTime() - 1);
527
-
528
- session
529
- .sessions()
530
- .getUserSessionsStream(realm, user)
531
- .collect(
532
- Collectors
533
- .toList()) // collect to avoid concurrent modification as backchannelLogout removes
534
- // the user sessions.
535
- .forEach(
536
- userSession ->
537
- AuthenticationManager.backchannelLogout(
538
- session,
539
- realm,
540
- userSession,
541
- session.getContext().getUri(),
542
- clientConnection,
543
- headers,
544
- true));
545
-
546
- UriBuilder builder =
547
- AccountUrls.accountBase(session.getContext().getUri().getBaseUri())
548
- .path(AccountFormService.class, "sessionsPage");
549
- String referrer = session.getContext().getUri().getQueryParameters().getFirst("referrer");
550
- if (referrer != null) {
551
- builder.queryParam("referrer", referrer);
552
- }
553
- URI location = builder.build(realm.getName());
554
- return Response.seeOther(location).build();
555
- }
556
-
557
- @Path("applications")
558
- @POST
559
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
560
- public Response processRevokeGrant() {
561
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
562
-
563
- if (auth == null) {
564
- return login("applications");
565
- }
566
-
567
- auth.require(AccountRoles.MANAGE_ACCOUNT);
568
- csrfCheck(formData);
569
-
570
- String clientId = formData.getFirst("clientId");
571
- if (clientId == null) {
572
- setReferrerOnPage();
573
- return account
574
- .setError(Response.Status.BAD_REQUEST, Messages.CLIENT_NOT_FOUND)
575
- .createResponse(AccountPages.APPLICATIONS);
576
- }
577
- ClientModel client = realm.getClientById(clientId);
578
- if (client == null) {
579
- setReferrerOnPage();
580
- return account
581
- .setError(Response.Status.BAD_REQUEST, Messages.CLIENT_NOT_FOUND)
582
- .createResponse(AccountPages.APPLICATIONS);
583
- }
584
-
585
- // Revoke grant in UserModel
586
- UserModel user = auth.getUser();
587
- UserConsentManager.revokeConsentToClient(session, client, user);
588
-
589
- event
590
- .event(EventType.REVOKE_GRANT)
591
- .client(auth.getClient())
592
- .user(auth.getUser())
593
- .detail(Details.REVOKED_CLIENT, client.getClientId())
594
- .success();
595
- setReferrerOnPage();
596
-
597
- UriBuilder builder =
598
- AccountUrls.accountBase(session.getContext().getUri().getBaseUri())
599
- .path(AccountFormService.class, "applicationsPage");
600
- String referrer = session.getContext().getUri().getQueryParameters().getFirst("referrer");
601
- if (referrer != null) {
602
- builder.queryParam("referrer", referrer);
603
- }
604
- URI location = builder.build(realm.getName());
605
- return Response.seeOther(location).build();
606
- }
607
-
608
- /**
609
- * Update the TOTP for this account.
610
- *
611
- * <p>form parameters:
612
- *
613
- * <p>totp - otp generated by authenticator totpSecret - totp secret to register
614
- *
615
- * @return
616
- */
617
- @Path("totp")
618
- @POST
619
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
620
- public Response processTotpUpdate() {
621
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
622
-
623
- if (auth == null) {
624
- return login("totp");
625
- }
626
-
627
- auth.require(AccountRoles.MANAGE_ACCOUNT);
628
-
629
- account.setAttribute(
630
- "mode", session.getContext().getUri().getQueryParameters().getFirst("mode"));
631
-
632
- String action = formData.getFirst("submitAction");
633
- if (action != null && action.equals("Cancel")) {
634
- setReferrerOnPage();
635
- return account.createResponse(AccountPages.TOTP);
636
- }
637
-
638
- csrfCheck(formData);
639
-
640
- UserModel user = auth.getUser();
641
-
642
- if (action != null && action.equals("Delete")) {
643
- String credentialId = formData.getFirst("credentialId");
644
- if (credentialId == null) {
645
- setReferrerOnPage();
646
- return account
647
- .setError(Status.OK, Messages.UNEXPECTED_ERROR_HANDLING_REQUEST)
648
- .createResponse(AccountPages.TOTP);
649
- }
650
- CredentialHelper.deleteOTPCredential(session, realm, user, credentialId);
651
- event.event(EventType.REMOVE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
652
- setReferrerOnPage();
653
- return account.setSuccess(Messages.SUCCESS_TOTP_REMOVED).createResponse(AccountPages.TOTP);
654
- } else {
655
- String challengeResponse = formData.getFirst("totp");
656
- String totpSecret = formData.getFirst("totpSecret");
657
- String userLabel = formData.getFirst("userLabel");
658
-
659
- OTPPolicy policy = realm.getOTPPolicy();
660
- OTPCredentialModel credentialModel =
661
- OTPCredentialModel.createFromPolicy(realm, totpSecret, userLabel);
662
- if (Validation.isBlank(challengeResponse)) {
663
- setReferrerOnPage();
664
- return account.setError(Status.OK, Messages.MISSING_TOTP).createResponse(AccountPages.TOTP);
665
- } else if (!CredentialValidation.validOTP(
666
- challengeResponse, credentialModel, policy.getLookAheadWindow())) {
667
- setReferrerOnPage();
668
- return account.setError(Status.OK, Messages.INVALID_TOTP).createResponse(AccountPages.TOTP);
669
- }
670
-
671
- if (!CredentialHelper.createOTPCredential(
672
- session, realm, user, challengeResponse, credentialModel)) {
673
- setReferrerOnPage();
674
- return account.setError(Status.OK, Messages.INVALID_TOTP).createResponse(AccountPages.TOTP);
675
- }
676
- event.event(EventType.UPDATE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
677
-
678
- setReferrerOnPage();
679
- return account.setSuccess(Messages.SUCCESS_TOTP).createResponse(AccountPages.TOTP);
680
- }
681
- }
682
-
683
- /**
684
- * Update account password
685
- *
686
- * <p>Form params:
687
- *
688
- * <p>password - old password password-new pasword-confirm
689
- *
690
- * @return
691
- */
692
- @Path("password")
693
- @POST
694
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
695
- public Response processPasswordUpdate() {
696
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
697
-
698
- if (auth == null) {
699
- return login("password");
700
- }
701
-
702
- auth.require(AccountRoles.MANAGE_ACCOUNT);
703
-
704
- csrfCheck(formData);
705
- UserModel user = auth.getUser();
706
-
707
- boolean requireCurrent = isPasswordSet(session, realm, user);
708
- account.setPasswordSet(requireCurrent);
709
-
710
- String password = formData.getFirst("password");
711
- String passwordNew = formData.getFirst("password-new");
712
- String passwordConfirm = formData.getFirst("password-confirm");
713
-
714
- EventBuilder errorEvent =
715
- event
716
- .clone()
717
- .event(EventType.UPDATE_PASSWORD_ERROR)
718
- .client(auth.getClient())
719
- .user(auth.getSession().getUser());
720
-
721
- if (requireCurrent) {
722
- if (Validation.isBlank(password)) {
723
- setReferrerOnPage();
724
- errorEvent.error(Errors.PASSWORD_MISSING);
725
- return account
726
- .setError(Status.OK, Messages.MISSING_PASSWORD)
727
- .createResponse(AccountPages.PASSWORD);
728
- }
729
-
730
- UserCredentialModel cred = UserCredentialModel.password(password);
731
- if (!user.credentialManager().isValid(cred)) {
732
- setReferrerOnPage();
733
- errorEvent.error(Errors.INVALID_USER_CREDENTIALS);
734
- return account
735
- .setError(Status.OK, Messages.INVALID_PASSWORD_EXISTING)
736
- .createResponse(AccountPages.PASSWORD);
737
- }
738
- }
739
-
740
- if (Validation.isBlank(passwordNew)) {
741
- setReferrerOnPage();
742
- errorEvent.error(Errors.PASSWORD_MISSING);
743
- return account
744
- .setError(Status.OK, Messages.MISSING_PASSWORD)
745
- .createResponse(AccountPages.PASSWORD);
746
- }
747
-
748
- if (!passwordNew.equals(passwordConfirm)) {
749
- setReferrerOnPage();
750
- errorEvent.error(Errors.PASSWORD_CONFIRM_ERROR);
751
- return account
752
- .setError(Status.OK, Messages.INVALID_PASSWORD_CONFIRM)
753
- .createResponse(AccountPages.PASSWORD);
754
- }
755
-
756
- try {
757
- user.credentialManager().updateCredential(UserCredentialModel.password(passwordNew, false));
758
- } catch (ReadOnlyException mre) {
759
- setReferrerOnPage();
760
- errorEvent.error(Errors.NOT_ALLOWED);
761
- return account
762
- .setError(Response.Status.BAD_REQUEST, Messages.READ_ONLY_PASSWORD)
763
- .createResponse(AccountPages.PASSWORD);
764
- } catch (ModelException me) {
765
- ServicesLogger.LOGGER.failedToUpdatePassword(me);
766
- setReferrerOnPage();
767
- errorEvent.detail(Details.REASON, me.getMessage()).error(Errors.PASSWORD_REJECTED);
768
- return account
769
- .setError(Response.Status.NOT_ACCEPTABLE, me.getMessage(), me.getParameters())
770
- .createResponse(AccountPages.PASSWORD);
771
- } catch (Exception ape) {
772
- ServicesLogger.LOGGER.failedToUpdatePassword(ape);
773
- setReferrerOnPage();
774
- errorEvent.detail(Details.REASON, ape.getMessage()).error(Errors.PASSWORD_REJECTED);
775
- return account
776
- .setError(Response.Status.INTERNAL_SERVER_ERROR, ape.getMessage())
777
- .createResponse(AccountPages.PASSWORD);
778
- }
779
-
780
- session
781
- .sessions()
782
- .getUserSessionsStream(realm, user)
783
- .filter(s -> !Objects.equals(s.getId(), auth.getSession().getId()))
784
- .collect(
785
- Collectors
786
- .toList()) // collect to avoid concurrent modification as backchannelLogout removes
787
- // the user sessions.
788
- .forEach(
789
- s ->
790
- AuthenticationManager.backchannelLogout(
791
- session,
792
- realm,
793
- s,
794
- session.getContext().getUri(),
795
- clientConnection,
796
- headers,
797
- true));
798
-
799
- event.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();
800
-
801
- setReferrerOnPage();
802
- return account
803
- .setPasswordSet(true)
804
- .setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
805
- .createResponse(AccountPages.PASSWORD);
806
- }
807
-
808
- @Path("identity")
809
- @POST
810
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
811
- public Response processFederatedIdentityUpdate() {
812
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
813
-
814
- if (auth == null) {
815
- return login("identity");
816
- }
817
-
818
- auth.require(AccountRoles.MANAGE_ACCOUNT);
819
- csrfCheck(formData);
820
- UserModel user = auth.getUser();
821
-
822
- String action = formData.getFirst("action");
823
- String providerId = formData.getFirst("providerId");
824
-
825
- if (Validation.isEmpty(providerId)) {
826
- setReferrerOnPage();
827
- return account
828
- .setError(Status.OK, Messages.MISSING_IDENTITY_PROVIDER)
829
- .createResponse(AccountPages.FEDERATED_IDENTITY);
830
- }
831
- AccountSocialAction accountSocialAction = AccountSocialAction.getAction(action);
832
- if (accountSocialAction == null) {
833
- setReferrerOnPage();
834
- return account
835
- .setError(Status.OK, Messages.INVALID_FEDERATED_IDENTITY_ACTION)
836
- .createResponse(AccountPages.FEDERATED_IDENTITY);
837
- }
838
-
839
- if (!realm
840
- .getIdentityProvidersStream()
841
- .anyMatch(model -> Objects.equals(model.getAlias(), providerId))) {
842
- setReferrerOnPage();
843
- return account
844
- .setError(Status.OK, Messages.IDENTITY_PROVIDER_NOT_FOUND)
845
- .createResponse(AccountPages.FEDERATED_IDENTITY);
846
- }
847
-
848
- if (!user.isEnabled()) {
849
- setReferrerOnPage();
850
- return account
851
- .setError(Status.OK, Messages.ACCOUNT_DISABLED)
852
- .createResponse(AccountPages.FEDERATED_IDENTITY);
853
- }
854
-
855
- switch (accountSocialAction) {
856
- case ADD:
857
- String redirectUri =
858
- UriBuilder.fromUri(
859
- AccountUrls.accountFederatedIdentityPage(
860
- session.getContext().getUri().getBaseUri(), realm.getName()))
861
- .build()
862
- .toString();
863
-
864
- try {
865
- String nonce = UUID.randomUUID().toString();
866
- MessageDigest md = MessageDigest.getInstance("SHA-256");
867
- String input = nonce + auth.getSession().getId() + client.getClientId() + providerId;
868
- byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
869
- String hash = Base64Url.encode(check);
870
- URI linkUrl =
871
- AccountUrls.identityProviderLinkRequest(
872
- this.session.getContext().getUri().getBaseUri(), providerId, realm.getName());
873
- linkUrl =
874
- UriBuilder.fromUri(linkUrl)
875
- .queryParam("nonce", nonce)
876
- .queryParam("hash", hash)
877
- .queryParam("client_id", client.getClientId())
878
- .queryParam("redirect_uri", redirectUri)
879
- .build();
880
- return Response.seeOther(linkUrl).build();
881
- } catch (Exception spe) {
882
- setReferrerOnPage();
883
- return account
884
- .setError(
885
- Response.Status.INTERNAL_SERVER_ERROR, Messages.IDENTITY_PROVIDER_REDIRECT_ERROR)
886
- .createResponse(AccountPages.FEDERATED_IDENTITY);
887
- }
888
- case REMOVE:
889
- FederatedIdentityModel link = session.users().getFederatedIdentity(realm, user, providerId);
890
- if (link != null) {
891
-
892
- // Removing last social provider is not possible if you don't have other possibility to
893
- // authenticate
894
- if (session.users().getFederatedIdentitiesStream(realm, user).count() > 1
895
- || user.getFederationLink() != null
896
- || isPasswordSet(session, realm, user)) {
897
- session.users().removeFederatedIdentity(realm, user, providerId);
898
-
899
- logger.debugv(
900
- "Social provider {0} removed successfully from user {1}",
901
- providerId, user.getUsername());
902
-
903
- event
904
- .event(EventType.REMOVE_FEDERATED_IDENTITY)
905
- .client(auth.getClient())
906
- .user(auth.getUser())
907
- .detail(Details.USERNAME, auth.getUser().getUsername())
908
- .detail(Details.IDENTITY_PROVIDER, link.getIdentityProvider())
909
- .detail(Details.IDENTITY_PROVIDER_USERNAME, link.getUserName())
910
- .success();
911
-
912
- setReferrerOnPage();
913
- return account
914
- .setSuccess(Messages.IDENTITY_PROVIDER_REMOVED)
915
- .createResponse(AccountPages.FEDERATED_IDENTITY);
916
- } else {
917
- setReferrerOnPage();
918
- return account
919
- .setError(Status.OK, Messages.FEDERATED_IDENTITY_REMOVING_LAST_PROVIDER)
920
- .createResponse(AccountPages.FEDERATED_IDENTITY);
921
- }
922
- } else {
923
- setReferrerOnPage();
924
- return account
925
- .setError(Status.OK, Messages.FEDERATED_IDENTITY_NOT_ACTIVE)
926
- .createResponse(AccountPages.FEDERATED_IDENTITY);
927
- }
928
- default:
929
- throw new IllegalArgumentException();
930
- }
931
- }
932
-
933
- @Path("resource")
934
- @GET
935
- public Response resourcesPage(@QueryParam("resource_id") String resourceId) {
936
- return forwardToPage("resource", AccountPages.RESOURCES);
937
- }
938
-
939
- @Path("resource/{resource_id}")
940
- @GET
941
- public Response resourceDetailPage(@PathParam("resource_id") String resourceId) {
942
- return forwardToPage("resource", AccountPages.RESOURCE_DETAIL);
943
- }
944
-
945
- @Path("resource/{resource_id}/grant")
946
- @GET
947
- public Response resourceDetailPageAfterGrant(@PathParam("resource_id") String resourceId) {
948
- return resourceDetailPage(resourceId);
949
- }
950
-
951
- @Path("resource/{resource_id}/grant")
952
- @POST
953
- public Response grantPermission(
954
- @PathParam("resource_id") String resourceId,
955
- @FormParam("action") String action,
956
- @FormParam("permission_id") String[] permissionId,
957
- @FormParam("requester") String requester) {
958
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
959
-
960
- if (auth == null) {
961
- return login("resource");
962
- }
963
-
964
- auth.require(AccountRoles.MANAGE_ACCOUNT);
965
-
966
- csrfCheck(formData);
967
-
968
- AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
969
- PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
970
- Resource resource =
971
- authorization.getStoreFactory().getResourceStore().findById(realm, null, resourceId);
972
-
973
- if (resource == null) {
974
- throw ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
975
- }
976
-
977
- if (action == null) {
978
- throw ErrorResponse.error("Invalid action", Response.Status.BAD_REQUEST);
979
- }
980
-
981
- boolean isGrant = "grant".equals(action);
982
- boolean isDeny = "deny".equals(action);
983
- boolean isRevoke = "revoke".equals(action);
984
- boolean isRevokePolicy = "revokePolicy".equals(action);
985
- boolean isRevokePolicyAll = "revokePolicyAll".equals(action);
986
-
987
- if (isRevokePolicy || isRevokePolicyAll) {
988
- List<String> ids = new ArrayList<>(Arrays.asList(permissionId));
989
- Iterator<String> iterator = ids.iterator();
990
- PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
991
- ResourceServer resourceServer =
992
- authorization.getStoreFactory().getResourceServerStore().findByClient(client);
993
- Policy policy = null;
994
-
995
- while (iterator.hasNext()) {
996
- String id = iterator.next();
997
-
998
- if (!id.contains(":")) {
999
- policy = policyStore.findById(realm, resourceServer, id);
1000
- iterator.remove();
1001
- break;
1002
- }
1003
- }
1004
-
1005
- Set<Scope> scopesToKeep = new HashSet<>();
1006
-
1007
- if (isRevokePolicyAll) {
1008
- for (Scope scope : policy.getScopes()) {
1009
- policy.removeScope(scope);
1010
- }
1011
- } else {
1012
- for (String id : ids) {
1013
- scopesToKeep.add(
1014
- authorization
1015
- .getStoreFactory()
1016
- .getScopeStore()
1017
- .findById(realm, resourceServer, id.split(":")[1]));
1018
- }
1019
-
1020
- for (Scope scope : policy.getScopes()) {
1021
- if (!scopesToKeep.contains(scope)) {
1022
- policy.removeScope(scope);
1023
- }
1024
- }
1025
- }
1026
-
1027
- if (policy.getScopes().isEmpty()) {
1028
- for (Policy associated : policy.getAssociatedPolicies()) {
1029
- policyStore.delete(realm, associated.getId());
1030
- }
1031
-
1032
- policyStore.delete(realm, policy.getId());
1033
- }
1034
- } else {
1035
- Map<PermissionTicket.FilterOption, String> filters =
1036
- new EnumMap<>(PermissionTicket.FilterOption.class);
1037
-
1038
- filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
1039
- filters.put(
1040
- PermissionTicket.FilterOption.REQUESTER,
1041
- session.users().getUserByUsername(realm, requester).getId());
1042
-
1043
- if (isRevoke) {
1044
- filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
1045
- } else {
1046
- filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
1047
- }
1048
-
1049
- List<PermissionTicket> tickets =
1050
- ticketStore.find(realm, resource.getResourceServer(), filters, null, null);
1051
- Iterator<PermissionTicket> iterator = tickets.iterator();
1052
-
1053
- while (iterator.hasNext()) {
1054
- PermissionTicket ticket = iterator.next();
1055
-
1056
- if (isGrant) {
1057
- if (permissionId != null
1058
- && permissionId.length > 0
1059
- && !Arrays.asList(permissionId).contains(ticket.getId())) {
1060
- continue;
1061
- }
1062
- }
1063
-
1064
- if (isGrant && !ticket.isGranted()) {
1065
- ticket.setGrantedTimestamp(System.currentTimeMillis());
1066
- iterator.remove();
1067
- } else if (isDeny || isRevoke) {
1068
- if (permissionId != null
1069
- && permissionId.length > 0
1070
- && Arrays.asList(permissionId).contains(ticket.getId())) {
1071
- iterator.remove();
1072
- }
1073
- }
1074
- }
1075
-
1076
- for (PermissionTicket ticket : tickets) {
1077
- ticketStore.delete(client.getRealm(), ticket.getId());
1078
- }
1079
- }
1080
-
1081
- if (isRevoke || isRevokePolicy || isRevokePolicyAll) {
1082
- return forwardToPage("resource", AccountPages.RESOURCE_DETAIL);
1083
- }
1084
-
1085
- return forwardToPage("resource", AccountPages.RESOURCES);
1086
- }
1087
-
1088
- @Path("resource/{resource_id}/share")
1089
- @GET
1090
- public Response resourceDetailPageAfterShare(@PathParam("resource_id") String resourceId) {
1091
- return resourceDetailPage(resourceId);
1092
- }
1093
-
1094
- @Path("resource/{resource_id}/share")
1095
- @POST
1096
- public Response shareResource(
1097
- @PathParam("resource_id") String resourceId,
1098
- @FormParam("user_id") String[] userIds,
1099
- @FormParam("scope_id") String[] scopes) {
1100
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
1101
-
1102
- if (auth == null) {
1103
- return login("resource");
1104
- }
1105
-
1106
- auth.require(AccountRoles.MANAGE_ACCOUNT);
1107
-
1108
- csrfCheck(formData);
1109
-
1110
- AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
1111
- PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
1112
- ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
1113
- Resource resource =
1114
- authorization.getStoreFactory().getResourceStore().findById(realm, null, resourceId);
1115
- ResourceServer resourceServer = resource.getResourceServer();
1116
-
1117
- if (resource == null) {
1118
- throw ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
1119
- }
1120
-
1121
- if (userIds == null || userIds.length == 0) {
1122
- setReferrerOnPage();
1123
- return account
1124
- .setError(Status.BAD_REQUEST, Messages.MISSING_PASSWORD)
1125
- .createResponse(AccountPages.PASSWORD);
1126
- }
1127
-
1128
- for (String id : userIds) {
1129
- UserModel user = session.users().getUserById(realm, id);
1130
-
1131
- if (user == null) {
1132
- user = session.users().getUserByUsername(realm, id);
1133
- }
1134
-
1135
- if (user == null) {
1136
- user = session.users().getUserByEmail(realm, id);
1137
- }
1138
-
1139
- if (user == null) {
1140
- setReferrerOnPage();
1141
- return account
1142
- .setError(Status.BAD_REQUEST, Messages.INVALID_USER)
1143
- .createResponse(AccountPages.RESOURCE_DETAIL);
1144
- }
1145
-
1146
- Map<PermissionTicket.FilterOption, String> filters =
1147
- new EnumMap<>(PermissionTicket.FilterOption.class);
1148
-
1149
- filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
1150
- filters.put(PermissionTicket.FilterOption.OWNER, auth.getUser().getId());
1151
- filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
1152
-
1153
- List<PermissionTicket> tickets = ticketStore.find(realm, resourceServer, filters, null, null);
1154
- final String userId = user.getId();
1155
-
1156
- if (tickets.isEmpty()) {
1157
- if (scopes != null && scopes.length > 0) {
1158
- for (String scopeId : scopes) {
1159
- Scope scope = scopeStore.findById(realm, resourceServer, scopeId);
1160
- PermissionTicket ticket = ticketStore.create(resourceServer, resource, scope, userId);
1161
- ticket.setGrantedTimestamp(System.currentTimeMillis());
1162
- }
1163
- } else {
1164
- if (resource.getScopes().isEmpty()) {
1165
- PermissionTicket ticket = ticketStore.create(resourceServer, resource, null, userId);
1166
- ticket.setGrantedTimestamp(System.currentTimeMillis());
1167
- } else {
1168
- for (Scope scope : resource.getScopes()) {
1169
- PermissionTicket ticket = ticketStore.create(resourceServer, resource, scope, userId);
1170
- ticket.setGrantedTimestamp(System.currentTimeMillis());
1171
- }
1172
- }
1173
- }
1174
- } else if (scopes != null && scopes.length > 0) {
1175
- List<String> grantScopes = new ArrayList<>(Arrays.asList(scopes));
1176
- Set<String> alreadyGrantedScopes =
1177
- tickets.stream()
1178
- .map(PermissionTicket::getScope)
1179
- .map(Scope::getId)
1180
- .collect(Collectors.toSet());
1181
-
1182
- grantScopes.removeIf(alreadyGrantedScopes::contains);
1183
-
1184
- for (String scopeId : grantScopes) {
1185
- Scope scope = scopeStore.findById(realm, resourceServer, scopeId);
1186
- PermissionTicket ticket = ticketStore.create(resourceServer, resource, scope, userId);
1187
- ticket.setGrantedTimestamp(System.currentTimeMillis());
1188
- }
1189
- }
1190
- }
1191
-
1192
- return forwardToPage("resource", AccountPages.RESOURCE_DETAIL);
1193
- }
1194
-
1195
- @Path("resource")
1196
- @POST
1197
- public Response processResourceActions(
1198
- @FormParam("resource_id") String[] resourceIds, @FormParam("action") String action) {
1199
- MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
1200
-
1201
- if (auth == null) {
1202
- return login("resource");
1203
- }
1204
-
1205
- auth.require(AccountRoles.MANAGE_ACCOUNT);
1206
- csrfCheck(formData);
1207
-
1208
- AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
1209
- PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
1210
-
1211
- if (action == null) {
1212
- throw ErrorResponse.error("Invalid action", Response.Status.BAD_REQUEST);
1213
- }
1214
-
1215
- for (String resourceId : resourceIds) {
1216
- Resource resource =
1217
- authorization.getStoreFactory().getResourceStore().findById(realm, null, resourceId);
1218
-
1219
- if (resource == null) {
1220
- throw ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
1221
- }
1222
-
1223
- Map<PermissionTicket.FilterOption, String> filters =
1224
- new EnumMap<>(PermissionTicket.FilterOption.class);
1225
-
1226
- filters.put(PermissionTicket.FilterOption.REQUESTER, auth.getUser().getId());
1227
- filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
1228
-
1229
- if ("cancel".equals(action)) {
1230
- filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
1231
- } else if ("cancelRequest".equals(action)) {
1232
- filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
1233
- }
1234
-
1235
- RealmModel realm = resource.getResourceServer().getRealm();
1236
- for (PermissionTicket ticket :
1237
- ticketStore.find(realm, resource.getResourceServer(), filters, null, null)) {
1238
- ticketStore.delete(realm, ticket.getId());
1239
- }
1240
- }
1241
-
1242
- return forwardToPage("authorization", AccountPages.RESOURCES);
1243
- }
1244
-
1245
- public static UriBuilder loginRedirectUrl(UriBuilder base) {
1246
- return RealmsResource.accountUrl(base).path(AccountFormService.class, "loginRedirect");
1247
- }
1248
-
1249
- @Override
1250
- protected URI getBaseRedirectUri() {
1251
- return AccountUrls.accountBase(session.getContext().getUri().getBaseUri())
1252
- .path("/")
1253
- .build(realm.getName());
1254
- }
1255
-
1256
- public static boolean isPasswordSet(KeycloakSession session, RealmModel realm, UserModel user) {
1257
- return user.credentialManager().isConfiguredFor(PasswordCredentialModel.TYPE);
1258
- }
1259
-
1260
- private String[] getReferrer() {
1261
- String referrer = session.getContext().getUri().getQueryParameters().getFirst("referrer");
1262
- if (referrer == null) {
1263
- return null;
1264
- }
1265
-
1266
- String referrerUri =
1267
- session.getContext().getUri().getQueryParameters().getFirst("referrer_uri");
1268
-
1269
- ClientModel referrerClient = realm.getClientByClientId(referrer);
1270
- if (referrerClient != null) {
1271
- if (referrerUri != null) {
1272
- referrerUri = RedirectUtils.verifyRedirectUri(session, referrerUri, referrerClient);
1273
- } else {
1274
- referrerUri =
1275
- ResolveRelative.resolveRelativeUri(
1276
- session, referrerClient.getRootUrl(), referrerClient.getBaseUrl());
1277
- }
1278
-
1279
- if (referrerUri != null) {
1280
- String referrerName = referrerClient.getName();
1281
- if (Validation.isBlank(referrerName)) {
1282
- referrerName = referrer;
1283
- }
1284
- return new String[] {referrerName, referrerUri};
1285
- }
1286
- } else if (referrerUri != null) {
1287
- if (client != null) {
1288
- referrerUri = RedirectUtils.verifyRedirectUri(session, referrerUri, client);
1289
-
1290
- if (referrerUri != null) {
1291
- return new String[] {referrer, referrerUri};
1292
- }
1293
- }
1294
- }
1295
-
1296
- return null;
1297
- }
1298
-
1299
- private enum AccountSocialAction {
1300
- ADD,
1301
- REMOVE;
1302
-
1303
- public static AccountSocialAction getAction(String action) {
1304
- if ("add".equalsIgnoreCase(action)) {
1305
- return ADD;
1306
- } else if ("remove".equalsIgnoreCase(action)) {
1307
- return REMOVE;
1308
- } else {
1309
- return null;
1310
- }
1311
- }
1312
- }
1313
-
1314
- private void csrfCheck(final MultivaluedMap<String, String> formData) {
1315
- String formStateChecker = formData.getFirst("stateChecker");
1316
- if (formStateChecker == null || !formStateChecker.equals(this.stateChecker)) {
1317
- throw new ForbiddenException();
1318
- }
1319
- }
1320
- }