Skip to content

Commit

Permalink
The bare minimum implementation for organization
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Igor <[email protected]>

Co-authored-by: vramik <[email protected]>
  • Loading branch information
pedroigor and vramik committed Mar 15, 2024
1 parent 0e717f7 commit 61ecf2d
Show file tree
Hide file tree
Showing 20 changed files with 943 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.representations.idm;

public class OrganizationRepresentation {

private String id;
private String name;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!(o instanceof OrganizationRepresentation)) return false;

OrganizationRepresentation that = (OrganizationRepresentation) o;

return id != null && id.equals(that.getId());
}

@Override
public int hashCode() {
if (id == null) {
return super.hashCode();
}
return id.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.admin.client.resource;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.keycloak.representations.idm.OrganizationRepresentation;

public interface OrganizationResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
OrganizationRepresentation toRepresentation();

@PUT
@Consumes(MediaType.APPLICATION_JSON)
Response update(OrganizationRepresentation organization);

@DELETE
Response delete();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.admin.client.resource;

import java.util.List;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.keycloak.representations.idm.OrganizationRepresentation;

public interface OrganizationsResource {

@POST
@Consumes(MediaType.APPLICATION_JSON)
Response create(OrganizationRepresentation organization);

@Path("{id}")
OrganizationResource get(@PathParam("id") String id);

@GET
@Produces(MediaType.APPLICATION_JSON)
List<OrganizationRepresentation> getAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,7 @@ Response testLDAPConnection(@FormParam("action") String action, @FormParam("conn

@Path("client-policies/profiles")
ClientPoliciesProfilesResource clientPoliciesProfilesResource();

@Path("organizations")
OrganizationsResource organizations();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.models.jpa.entities;

import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;

@Table(name="ORGANIZATION")
@Entity
@NamedQueries({
@NamedQuery(name="deleteByRealm", query="delete from OrganizationEntity o where o.realmId = :realmId"),
@NamedQuery(name="getByRealm", query="select o.id from OrganizationEntity o where o.realmId = :realmId")
})
public class OrganizationEntity {

@Id
@Column(name="ID", length = 36)
@Access(AccessType.PROPERTY)
protected String id;

@Column(name = "REALM_ID")
private String realmId;

@Column(name="NAME")
protected String name;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getRealmId() {
return realmId;
}

public void setRealmId(String realm) {
this.realmId = realm;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!(o instanceof OrganizationEntity)) return false;

OrganizationEntity that = (OrganizationEntity) o;

return id != null && id.equals(that.getId());
}

@Override
public int hashCode() {
if (id == null) {
return super.hashCode();
}
return id.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.organization.jpa;

import static org.keycloak.utils.StreamsUtil.closing;

import java.util.stream.Stream;

import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.jpa.entities.OrganizationEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.organization.OrganizationProvider;

public class JpaOrganizationProvider implements OrganizationProvider {

private final EntityManager em;

public JpaOrganizationProvider(KeycloakSession session) {
JpaConnectionProvider jpaProvider = session.getProvider(JpaConnectionProvider.class);
this.em = jpaProvider.getEntityManager();
}

@Override
public OrganizationModel createOrganization(RealmModel realm, String name) {
throwExceptionIfRealmIsNull(realm);
OrganizationEntity entity = new OrganizationEntity();

entity.setId(KeycloakModelUtils.generateId());
entity.setRealmId(realm.getId());
entity.setName(name);

em.persist(entity);

return new OrganizationAdapter(entity);
}

@Override
public boolean removeOrganization(RealmModel realm, OrganizationModel organization) {
throwExceptionIfRealmIsNull(realm);
throwExceptionIfOrganizationIsNull(organization);
OrganizationAdapter toRemove = getAdapter(realm, organization.getId());
throwExceptionIfOrganizationIsNull(toRemove);

if (!toRemove.getRealm().equals(realm.getId())) {
throw new IllegalArgumentException("Organization [" + organization.getId() + " does not belong to realm [" + realm.getId() + "]");
}

em.remove(toRemove.getEntity());

return true;
}

@Override
public void removeOrganizations(RealmModel realm) {
throwExceptionIfRealmIsNull(realm);
Query query = em.createNamedQuery("deleteByRealm");

query.setParameter("realmId", realm.getId());

query.executeUpdate();
}

@Override
public OrganizationModel getOrganizationById(RealmModel realm, String id) {
return getAdapter(realm, id);
}

@Override
public Stream<OrganizationModel> getOrganizationsStream(RealmModel realm) {
TypedQuery<String> query = em.createNamedQuery("getByRealm", String.class);

query.setParameter("realmId", realm.getId());

return closing(query.getResultStream().map(id -> getAdapter(realm, id)));
}

@Override
public void close() {

}

private OrganizationAdapter getAdapter(RealmModel realm, String id) {
OrganizationEntity entity = em.find(OrganizationEntity.class, id);

if (entity == null) {
return null;
}

if (!realm.getId().equals(entity.getRealmId())) {
return null;
}

return new OrganizationAdapter(entity);
}

private void throwExceptionIfOrganizationIsNull(OrganizationModel organization) {
if (organization == null) {
throw new IllegalArgumentException("organization can not be null");
}
}

private void throwExceptionIfRealmIsNull(RealmModel realm) {
if (realm == null) {
throw new IllegalArgumentException("realm can not be null");
}
}
}
Loading

0 comments on commit 61ecf2d

Please sign in to comment.