/** * Create a new authentication flow * * @param flow Authentication flow representation * @return */ @Path("/flows") @POST @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response createFlow(AuthenticationFlowRepresentation flow) { auth.requireManage(); if (flow.getAlias() == null || flow.getAlias().isEmpty()) { return ErrorResponse.exists("Failed to create flow with empty alias name"); } if (realm.getFlowByAlias(flow.getAlias()) != null) { return ErrorResponse.exists("Flow " + flow.getAlias() + " already exists"); } AuthenticationFlowModel createdModel = realm.addAuthenticationFlow(RepresentationToModel.toModel(flow)); flow.setId(createdModel.getId()); adminEvent .operation(OperationType.CREATE) .resourcePath(uriInfo, createdModel.getId()) .representation(flow) .success(); return Response.status(201).build(); }
private ClientSessionModel createClientSession( UserModel user, String redirectUri, String clientId) { if (!user.isEnabled()) { throw new WebApplicationException( ErrorResponse.error("User is disabled", Response.Status.BAD_REQUEST)); } if (redirectUri != null && clientId == null) { throw new WebApplicationException( ErrorResponse.error("Client id missing", Response.Status.BAD_REQUEST)); } if (clientId == null) { clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; } ClientModel client = realm.getClientByClientId(clientId); if (client == null || !client.isEnabled()) { throw new WebApplicationException( ErrorResponse.error(clientId + " not enabled", Response.Status.BAD_REQUEST)); } String redirect; if (redirectUri != null) { redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client); if (redirect == null) { throw new WebApplicationException( ErrorResponse.error("Invalid redirect uri.", Response.Status.BAD_REQUEST)); } } else { redirect = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString(); } UserSessionModel userSession = session .sessions() .createUserSession( realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "form", false, null, null); // audit.session(userSession); ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL); clientSession.setRedirectUri(redirect); clientSession.setUserSession(userSession); return clientSession; }
/** * Send a update account email to the user * * <p>An email contains a link the user can click to perform a set of required actions. The * redirectUri and clientId parameters are optional. The default for the redirect is the account * client. * * @param id User is * @param redirectUri Redirect uri * @param clientId Client id * @param actions required actions the user needs to complete * @return */ @Path("{id}/execute-actions-email") @PUT @Consumes(MediaType.APPLICATION_JSON) public Response executeActionsEmail( @PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId, List<String> actions) { auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { return ErrorResponse.error("User not found", Response.Status.NOT_FOUND); } if (user.getEmail() == null) { return ErrorResponse.error("User email missing", Response.Status.BAD_REQUEST); } ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId); for (String action : actions) { clientSession.addRequiredAction(action); } ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession); accessCode.setAction(ClientSessionModel.Action.EXECUTE_ACTIONS.name()); try { UriBuilder builder = Urls.executeActionsBuilder(uriInfo.getBaseUri()); builder.queryParam("key", accessCode.getCode()); String link = builder.build(realm.getName()).toString(); long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()); this.session .getProvider(EmailTemplateProvider.class) .setRealm(realm) .setUser(user) .sendExecuteActions(link, expiration); // audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, // accessCode.getCodeId()).success(); adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success(); return Response.ok().build(); } catch (EmailException e) { logger.failedToSendActionsEmail(e); return ErrorResponse.error( "Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR); } }
/** * Import a realm * * <p>Imports a realm from a full representation of that realm. Realm name must be unique. * * @param uriInfo * @param rep JSON representation of the realm * @return */ @POST @Consumes(MediaType.APPLICATION_JSON) public Response importRealm(@Context final UriInfo uriInfo, final RealmRepresentation rep) { RealmManager realmManager = new RealmManager(session); realmManager.setContextPath(keycloak.getContextPath()); if (!auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) { throw new ForbiddenException(); } if (!auth.hasRealmRole(AdminRoles.CREATE_REALM)) { throw new ForbiddenException(); } logger.debugv("importRealm: {0}", rep.getRealm()); try { RealmModel realm = realmManager.importRealm(rep); grantPermissionsToRealmCreator(realm); URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build(); logger.debugv("imported realm success, sending back: {0}", location.toString()); return Response.created(location).build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("Realm with same name exists"); } }
/** * Update the client * * @param rep * @return */ @PUT @Consumes(MediaType.APPLICATION_JSON) public Response update(final ClientRepresentation rep) { auth.requireManage(); if (client == null) { throw new NotFoundException("Could not find client"); } ValidationMessages validationMessages = new ValidationMessages(); if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); throw new ErrorResponseException( validationMessages.getStringMessages(), validationMessages.getStringMessages(messages), Response.Status.BAD_REQUEST); } try { updateClientFromRep(rep, client, session); adminEvent .operation(OperationType.UPDATE) .resourcePath(uriInfo) .representation(rep) .success(); return Response.noContent().build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("Client " + rep.getClientId() + " already exists"); } }
/** * Update the user * * @param id User id * @param rep * @return */ @Path("{id}") @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateUser(final @PathParam("id") String id, final UserRepresentation rep) { auth.requireManage(); try { UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } Set<String> attrsToRemove; if (rep.getAttributes() != null) { attrsToRemove = new HashSet<>(user.getAttributes().keySet()); attrsToRemove.removeAll(rep.getAttributes().keySet()); } else { attrsToRemove = Collections.emptySet(); } if (rep.isEnabled() != null && rep.isEnabled()) { UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase()); if (failureModel != null) { failureModel.clearFailures(); } } updateUserFromRep(user, rep, attrsToRemove, realm, session); adminEvent .operation(OperationType.UPDATE) .resourcePath(uriInfo) .representation(rep) .success(); if (session.getTransaction().isActive()) { session.getTransaction().commit(); } return Response.noContent().build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("User exists with same username or email"); } catch (ModelReadOnlyException re) { return ErrorResponse.exists("User is read only!"); } }
/** * Update the top-level information of the realm * * <p>Any user, roles or client information in the representation will be ignored. This will only * update top-level attributes of the realm. * * @param rep * @return */ @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateRealm(final RealmRepresentation rep) { auth.requireManage(); logger.debug("updating realm: " + realm.getName()); if (Config.getAdminRealm().equals(realm.getName()) && (rep.getRealm() != null && !rep.getRealm().equals(Config.getAdminRealm()))) { return ErrorResponse.error("Can't rename master realm", Status.BAD_REQUEST); } try { if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) { try { KeyPairVerifier.verify(rep.getPrivateKey(), rep.getPublicKey()); } catch (VerificationException e) { return ErrorResponse.error(e.getMessage(), Status.BAD_REQUEST); } } if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getCertificate() != null)) { try { X509Certificate cert = PemUtils.decodeCertificate(rep.getCertificate()); if (cert == null) { return ErrorResponse.error("Failed to decode certificate", Status.BAD_REQUEST); } } catch (Exception e) { return ErrorResponse.error("Failed to decode certificate", Status.BAD_REQUEST); } } RepresentationToModel.updateRealm(rep, realm, session); // Refresh periodic sync tasks for configured federationProviders List<UserStorageProviderModel> federationProviders = realm.getUserStorageProviders(); UserStorageSyncManager usersSyncManager = new UserStorageSyncManager(); for (final UserStorageProviderModel fedProvider : federationProviders) { usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false); } adminEvent .operation(OperationType.UPDATE) .representation(StripSecretsUtils.strip(rep)) .success(); return Response.noContent().build(); } catch (PatternSyntaxException e) { return ErrorResponse.error( "Specified regex pattern(s) is invalid.", Response.Status.BAD_REQUEST); } catch (ModelDuplicateException e) { return ErrorResponse.exists("Realm with same name exists"); } catch (Exception e) { logger.error(e.getMessage(), e); return ErrorResponse.error("Failed to update realm", Response.Status.INTERNAL_SERVER_ERROR); } }
/** * Update the top-level information of the realm * * <p>Any user, roles or client information in the representation will be ignored. This will only * update top-level attributes of the realm. * * @param rep * @return */ @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateRealm(final RealmRepresentation rep) { auth.requireManage(); logger.debug("updating realm: " + realm.getName()); try { RepresentationToModel.updateRealm(rep, realm); if (rep.isRealmCacheEnabled() != null && session.realms() instanceof CacheRealmProvider) { CacheRealmProvider cacheRealmProvider = (CacheRealmProvider) session.realms(); cacheRealmProvider.setEnabled(rep.isRealmCacheEnabled()); } if (rep.isUserCacheEnabled() != null && session.userStorage() instanceof CacheUserProvider) { CacheUserProvider cache = (CacheUserProvider) session.userStorage(); cache.setEnabled(rep.isUserCacheEnabled()); } // Refresh periodic sync tasks for configured federationProviders List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders(); UsersSyncManager usersSyncManager = new UsersSyncManager(); for (final UserFederationProviderModel fedProvider : federationProviders) { usersSyncManager.refreshPeriodicSyncForProvider( session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId()); } adminEvent.operation(OperationType.UPDATE).representation(rep).success(); return Response.noContent().build(); } catch (PatternSyntaxException e) { return ErrorResponse.error( "Specified regex pattern(s) is invalid.", Response.Status.BAD_REQUEST); } catch (ModelDuplicateException e) { throw e; } catch (Exception e) { logger.error(e); return ErrorResponse.error( "Failed to update " + rep.getRealm() + " Realm.", Response.Status.INTERNAL_SERVER_ERROR); } }
/** * Create a new user * * <p>Username must be unique. * * @param uriInfo * @param rep * @return */ @POST @Consumes(MediaType.APPLICATION_JSON) public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) { auth.requireManage(); // Double-check duplicated username and email here due to federation if (session.users().getUserByUsername(rep.getUsername(), realm) != null) { return ErrorResponse.exists("User exists with same username"); } if (rep.getEmail() != null && session.users().getUserByEmail(rep.getEmail(), realm) != null) { return ErrorResponse.exists("User exists with same email"); } try { UserModel user = session.users().addUser(realm, rep.getUsername()); Set<String> emptySet = Collections.emptySet(); updateUserFromRep(user, rep, emptySet, realm, session); adminEvent .operation(OperationType.CREATE) .resourcePath(uriInfo, user.getId()) .representation(rep) .success(); if (session.getTransaction().isActive()) { session.getTransaction().commit(); } return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getId()).build()).build(); } catch (ModelDuplicateException e) { if (session.getTransaction().isActive()) { session.getTransaction().setRollbackOnly(); } return ErrorResponse.exists("User exists with same username or email"); } }
/** * Test LDAP connection * * @param action * @param connectionUrl * @param bindDn * @param bindCredential * @return */ @Path("testLDAPConnection") @GET @NoCache public Response testLDAPConnection( @QueryParam("action") String action, @QueryParam("connectionUrl") String connectionUrl, @QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential) { auth.init(RealmAuth.Resource.REALM).requireManage(); boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential); return result ? Response.noContent().build() : ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST); }
/** * Delete the user * * @param id User id */ @Path("{id}") @DELETE @NoCache public Response deleteUser(final @PathParam("id") String id) { auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } boolean removed = new UserManager(session).removeUser(realm, user); if (removed) { adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success(); return Response.noContent().build(); } else { return ErrorResponse.error("User couldn't be deleted", Response.Status.BAD_REQUEST); } }
/** * Add a social login provider to the user * * @param id User id * @param provider Social login provider id * @param rep * @return */ @Path("{id}/federated-identity/{provider}") @POST @NoCache public Response addFederatedIdentity( final @PathParam("id") String id, final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) { auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } if (session.users().getFederatedIdentity(user, provider, realm) != null) { return ErrorResponse.exists("User is already linked with provider"); } FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName()); session.users().addFederatedIdentity(realm, user, socialLink); adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(rep).success(); return Response.noContent().build(); }