Support for transient brokering in admin console
Part-of: Add support for not importing brokered user into Keycloak database Closes: #11334
This commit is contained in:
parent
26328a7c1e
commit
a668c2cb2b
4 changed files with 82 additions and 36 deletions
|
@ -2871,6 +2871,7 @@
|
||||||
"disableUserInfo": "Disable user info",
|
"disableUserInfo": "Disable user info",
|
||||||
"isAccessTokenJWT": "Access Token is JWT",
|
"isAccessTokenJWT": "Access Token is JWT",
|
||||||
"userInfoUrl": "User Info URL",
|
"userInfoUrl": "User Info URL",
|
||||||
|
"doNotStoreUsers": "Do not store users",
|
||||||
"issuer": "Issuer",
|
"issuer": "Issuer",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"prompts": {
|
"prompts": {
|
||||||
|
@ -2954,6 +2955,7 @@
|
||||||
"disableUserInfoHelp": "Disable usage of User Info service to obtain additional user information? Default is to use this OIDC service.",
|
"disableUserInfoHelp": "Disable usage of User Info service to obtain additional user information? Default is to use this OIDC service.",
|
||||||
"isAccessTokenJWTHelp": "The Access Token received from the Identity Provider is a JWT and its claims will be accessible for mappers.",
|
"isAccessTokenJWTHelp": "The Access Token received from the Identity Provider is a JWT and its claims will be accessible for mappers.",
|
||||||
"userInfoUrlHelp": "The User Info Url. This is optional.",
|
"userInfoUrlHelp": "The User Info Url. This is optional.",
|
||||||
|
"doNotStoreUsersHelp": "When enabled, users from this broker are not persisted in internal database.",
|
||||||
"issuerHelp": "The issuer identifier for the issuer of the response. If not provided, no validation will be performed.",
|
"issuerHelp": "The issuer identifier for the issuer of the response. If not provided, no validation will be performed.",
|
||||||
"promptHelp": "Specifies whether the Authorization Server prompts the End-User for re-authentication and consent.",
|
"promptHelp": "Specifies whether the Authorization Server prompts the End-User for re-authentication and consent.",
|
||||||
"acceptsPromptNoneHelp": "This is just used together with Identity Provider Authenticator or when kc_idp_hint points to this identity provider. In case that client sends a request with prompt=none and user is not yet authenticated, the error will not be directly returned to client, but the request with prompt=none will be forwarded to this identity provider.",
|
"acceptsPromptNoneHelp": "This is just used together with Identity Provider Authenticator or when kc_idp_hint points to this identity provider. In case that client sends a request with prompt=none and user is not yet authenticated, the error will not be directly returned to client, but the request with prompt=none will be forwarded to this identity provider.",
|
||||||
|
|
|
@ -96,6 +96,7 @@ export const AdvancedSettings = ({ isOIDC, isSAML }: AdvancedSettingsProps) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
register,
|
register,
|
||||||
|
setValue,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useFormContext<IdentityProviderRepresentation>();
|
} = useFormContext<IdentityProviderRepresentation>();
|
||||||
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
||||||
|
@ -105,6 +106,12 @@ export const AdvancedSettings = ({ isOIDC, isSAML }: AdvancedSettingsProps) => {
|
||||||
defaultValue: "false",
|
defaultValue: "false",
|
||||||
});
|
});
|
||||||
const claimFilterRequired = filteredByClaim === "true";
|
const claimFilterRequired = filteredByClaim === "true";
|
||||||
|
const transientSessions = useWatch({
|
||||||
|
control,
|
||||||
|
name: "config.doNotStoreUsers",
|
||||||
|
defaultValue: "false",
|
||||||
|
});
|
||||||
|
const syncModeAvailable = transientSessions === "false";
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isOIDC && !isSAML && (
|
{!isOIDC && !isSAML && (
|
||||||
|
@ -231,6 +238,29 @@ export const AdvancedSettings = ({ isOIDC, isSAML }: AdvancedSettingsProps) => {
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormGroupField label="doNotStoreUsers">
|
||||||
|
<Controller
|
||||||
|
name="config.doNotStoreUsers"
|
||||||
|
defaultValue="false"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<Switch
|
||||||
|
id="doNotStoreUsers"
|
||||||
|
label={t("on")}
|
||||||
|
labelOff={t("off")}
|
||||||
|
isChecked={field.value === "true"}
|
||||||
|
onChange={(value) => {
|
||||||
|
field.onChange(value.toString());
|
||||||
|
// if field is checked, set sync mode to import
|
||||||
|
if (value) {
|
||||||
|
setValue("config.syncMode", "IMPORT");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormGroupField>
|
||||||
|
{syncModeAvailable && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
className="pf-u-pb-3xl"
|
className="pf-u-pb-3xl"
|
||||||
label={t("syncMode")}
|
label={t("syncMode")}
|
||||||
|
@ -271,6 +301,7 @@ export const AdvancedSettings = ({ isOIDC, isSAML }: AdvancedSettingsProps) => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.UserManager;
|
import org.keycloak.models.UserManager;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.light.LightweightUserAdapter;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
|
@ -615,7 +616,9 @@ public class UserResource {
|
||||||
public void logout() {
|
public void logout() {
|
||||||
auth.users().requireManage(user);
|
auth.users().requireManage(user);
|
||||||
|
|
||||||
|
if (! LightweightUserAdapter.isLightweightUser(user)) {
|
||||||
session.users().setNotBeforeForUser(realm, user, Time.currentTime());
|
session.users().setNotBeforeForUser(realm, user, Time.currentTime());
|
||||||
|
}
|
||||||
|
|
||||||
session.sessions().getUserSessionsStream(realm, user)
|
session.sessions().getUserSessionsStream(realm, user)
|
||||||
.collect(Collectors.toList()) // collect to avoid concurrent modification as backchannelLogout removes the user sessions.
|
.collect(Collectors.toList()) // collect to avoid concurrent modification as backchannelLogout removes the user sessions.
|
||||||
|
|
|
@ -34,6 +34,8 @@ import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.light.LightweightUserAdapter;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.policy.PasswordPolicyNotMetException;
|
import org.keycloak.policy.PasswordPolicyNotMetException;
|
||||||
|
@ -68,6 +70,7 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.keycloak.models.Constants.SESSION_NOTE_LIGHTWEIGHT_USER;
|
||||||
import static org.keycloak.models.utils.KeycloakModelUtils.findGroupByPath;
|
import static org.keycloak.models.utils.KeycloakModelUtils.findGroupByPath;
|
||||||
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
||||||
|
|
||||||
|
@ -225,7 +228,14 @@ public class UsersResource {
|
||||||
*/
|
*/
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
public UserResource user(final @PathParam("id") String id) {
|
public UserResource user(final @PathParam("id") String id) {
|
||||||
UserModel user = session.users().getUserById(realm, id);
|
UserModel user = null;
|
||||||
|
if (LightweightUserAdapter.isLightweightUser(id)) {
|
||||||
|
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, LightweightUserAdapter.getLightweightUserId(id));
|
||||||
|
user = userSession.getUser();
|
||||||
|
} else {
|
||||||
|
user = session.users().getUserById(realm, id);
|
||||||
|
}
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
// we do this to make sure somebody can't phish ids
|
// we do this to make sure somebody can't phish ids
|
||||||
if (auth.users().canQuery()) throw new NotFoundException("User not found");
|
if (auth.users().canQuery()) throw new NotFoundException("User not found");
|
||||||
|
|
Loading…
Reference in a new issue