/**
   * 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();
  }
예제 #2
0
  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;
  }
예제 #3
0
  /**
   * 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);
    }
  }
예제 #4
0
  /**
   * 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");
    }
  }
예제 #5
0
  /**
   * 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");
    }
  }
예제 #6
0
  /**
   * 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);
    }
  }
예제 #8
0
  /**
   * 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);
    }
  }
예제 #9
0
  /**
   * 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");
    }
  }
예제 #10
0
  /**
   * 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);
  }
예제 #11
0
  /**
   * 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);
    }
  }
예제 #12
0
  /**
   * 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();
  }