Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic auth(IAM) #230

Merged
merged 23 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ services:
- search_providerName=${SEARCH_PROVIDER_NAME-dev.sunbirdrc.registry.service.ElasticSearchService}
- sunbird_sso_realm=${KEYCLOAK_REALM-sunbird-rc}
- sunbird_sso_url=http://keycloak:8080/auth
- oauth2_resource_uri=http://keycloak:8080/auth/realms/sunbird-rc
- sunbird_sso_admin_client_id=${KEYCLOAK_ADMIN_CLIENT_ID-admin-api}
- sunbird_sso_client_id=${KEYCLOAK_CLIENT_ID-registry-frontend}
- sunbird_sso_admin_client_secret=${KEYCLOAK_SECRET}
Expand Down Expand Up @@ -84,6 +85,8 @@ services:
condition: service_healthy
db:
condition: service_healthy
keycloak:
condition: service_healthy
healthcheck:
test: [ "CMD-SHELL", "wget -nv -t1 --spider http://localhost:8081/health || exit 1" ]
interval: 30s
Expand Down
4 changes: 3 additions & 1 deletion java/middleware/pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand Down
76 changes: 76 additions & 0 deletions java/middleware/registry-middleware/auth0/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>registry-middleware</artifactId>
<groupId>dev.sunbirdrc</groupId>
<version>2.0.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>auth0</artifactId>

<dependencyManagement>
<dependencies>

</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>auth0</artifactId>
srprasanna marked this conversation as resolved.
Show resolved Hide resolved
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dev.sunbirdrc</groupId>
<artifactId>pojos</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>dev.sunbirdrc</groupId>
<artifactId>middleware-commons</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>dev.sunbirdrc</groupId>
<artifactId>identity-provider</artifactId>
<version>2.0.3</version>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package dev.sunbirdrc.auth.auth0;

import com.auth0.client.auth.AuthAPI;
import com.auth0.client.mgmt.ManagementAPI;
import com.auth0.client.mgmt.filter.RolesFilter;
import com.auth0.client.mgmt.filter.UserFilter;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
import com.auth0.json.mgmt.permissions.Permission;
import com.auth0.json.mgmt.resourceserver.ResourceServer;
import com.auth0.json.mgmt.resourceserver.Scope;
import com.auth0.json.mgmt.roles.Role;
import com.auth0.json.mgmt.roles.RolesPage;
import com.auth0.json.mgmt.users.User;
import com.auth0.json.mgmt.users.UsersPage;
import com.auth0.net.TokenRequest;
import dev.sunbirdrc.pojos.ComponentHealthInfo;
import dev.sunbirdrc.registry.identity_providers.pojos.IdentityProviderConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.sunbirdrc.registry.identity_providers.pojos.CreateUserRequest;
import dev.sunbirdrc.registry.identity_providers.pojos.IdentityException;
import dev.sunbirdrc.registry.identity_providers.pojos.IdentityManager;
import org.springframework.beans.factory.annotation.Value;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static dev.sunbirdrc.registry.middleware.util.Constants.CONNECTION_FAILURE;


public class Auth0AdminUtil implements IdentityManager {
private static final Logger logger = LoggerFactory.getLogger(Auth0AdminUtil.class);
private static final String EMAIL = "email_id";
private static final String ENTITY = "entity";
private static final String MOBILE_NUMBER = "mobile_number";
private static final String PASSWORD = "password";
public static final String SUNBIRD_AUTH0_SERVICE_NAME = "sunbird.auth0.service";
private final IdentityProviderConfiguration identityProviderConfiguration;

@Value("${auth0.resource.server.id}")
private String resourceServerId;

@Value("${auth0.resource.user.connection:Username-Password-Authentication}")
private String userConnection;


public Auth0AdminUtil(IdentityProviderConfiguration identityProviderConfiguration) {
this.identityProviderConfiguration = identityProviderConfiguration;
}


@Override
public String createUser(CreateUserRequest createUserRequest) throws IdentityException {
logger.info("Creating user with mobile_number : " + createUserRequest.getUserName());
try {
String token = getToken();
ManagementAPI mgmt = ManagementAPI.newBuilder(identityProviderConfiguration.getRealm(), token).build();
Optional<String> roleOptional = createOrGetRole(mgmt, createUserRequest);
if (roleOptional.isPresent()) {
Optional<String> userOptional = createOrGetUser(mgmt, createUserRequest, roleOptional.get());
srprasanna marked this conversation as resolved.
Show resolved Hide resolved
if (userOptional.isPresent()) {
return userOptional.get();
} else {
throw new IdentityException("Creating user failed");
}
}
} catch (Exception e) {
logger.error("Error creating user in auth0", e);
throw new IdentityException(String.format("Auth0 user creation error %s", e.getMessage()));
}
return "";
}

private Optional<String> createOrGetUser(ManagementAPI mgmt, CreateUserRequest createUserRequest, String roleId) throws Auth0Exception {
try {
User userObject = createUserObject(createUserRequest);
User userRepresentation = mgmt.users().create(userObject).execute().getBody();
assignUserRole(mgmt, roleId, userRepresentation.getId());
return Optional.ofNullable(userRepresentation.getId());
} catch (Exception e) {
logger.error("User creation exception, {}", e.getMessage());
}
UsersPage usersPage = searchUser(mgmt, createUserRequest);
if (usersPage.getItems().size() > 0) {
User existingUser = usersPage.getItems().get(0);
assignUserRole(mgmt, roleId, existingUser.getId());
return Optional.ofNullable(existingUser.getId());
}
return Optional.empty();
}

private void assignUserRole(ManagementAPI mgmt, String roleId, String userId) throws Auth0Exception {
mgmt.roles().assignUsers(roleId, Collections.singletonList(userId)).execute();
}

private UsersPage searchUser(ManagementAPI mgmt, CreateUserRequest createUserRequest) throws Auth0Exception {
UserFilter userFilter = new UserFilter();
userFilter.withQuery(createUserRequest.getUserName());
return mgmt.users().list(userFilter).execute().getBody();
}

private Optional<String> createOrGetRole(ManagementAPI mgmt, CreateUserRequest createUserRequest) throws Auth0Exception {
String role = createUserRequest.getEntity();
try {
Role roleRepresentation = createRole(mgmt, role);
addPermission(mgmt, role, roleRepresentation);
return Optional.ofNullable(roleRepresentation.getId());
} catch (Exception e) {
logger.error("Role creation exception, {}", e.getMessage());
}
RolesPage rolesPage = searchExistingRoles(mgmt, role);
if (rolesPage.getItems().size() > 0) {
Role existingRoleRepresentation = rolesPage.getItems().get(0);
addPermission(mgmt, role, existingRoleRepresentation);
return Optional.ofNullable(existingRoleRepresentation.getId());
}
return Optional.empty();
}

private void addScopeToResourceServer(ManagementAPI mgmt, String role) {
try {
ResourceServer resourceServer = new ResourceServer();
resourceServer.setScopes(Collections.singletonList(new Scope(role)));
mgmt.resourceServers().update(resourceServerId, resourceServer).execute();
} catch (Exception e) {
logger.error("Error adding scopes to resource server, {}", e.getMessage());
}
}

private RolesPage searchExistingRoles(ManagementAPI mgmt, String role) throws Auth0Exception {
RolesFilter filter = new RolesFilter();
filter.withName(role);
return mgmt.roles().list(filter).execute().getBody();
}

private Role createRole(ManagementAPI mgmt, String role) throws Auth0Exception {
Role roleRepresentation = new Role();
roleRepresentation.setName(role);
roleRepresentation = mgmt.roles().create(roleRepresentation).execute().getBody();
return roleRepresentation;
}

private void addPermission(ManagementAPI mgmt, String role, Role roleRepresentation) throws Auth0Exception {
addScopeToResourceServer(mgmt, role);
Permission permission = new Permission();
permission.setName(role);
permission.setResourceServerId(resourceServerId);
mgmt.roles().addPermissions(roleRepresentation.getId(), Collections.singletonList(permission)).execute();
}

private User createUserObject(CreateUserRequest createUserRequest) {
User user = new User();
user.setEmail(createUserRequest.getEmail());
// user.setPhoneNumber(createUserRequest.getMobile());
user.setName(createUserRequest.getUserName());
Map<String, Object> userMetadata = new HashMap<>();
userMetadata.put(ENTITY, Collections.singletonList(createUserRequest.getEntity()));
userMetadata.put(EMAIL, createUserRequest.getEmail());
userMetadata.put(MOBILE_NUMBER, createUserRequest.getMobile());
user.setUserMetadata(userMetadata);
user.setConnection("Username-Password-Authentication");
if (identityProviderConfiguration.setDefaultPassword()) {
user.setPassword(identityProviderConfiguration.getDefaultPassword().toCharArray());
}
return user;
}

//TODO: cache token
public String getToken() throws Auth0Exception {
AuthAPI authAPI = AuthAPI.newBuilder(identityProviderConfiguration.getRealm(),
identityProviderConfiguration.getClientId(), identityProviderConfiguration.getClientSecret()).build();
TokenRequest tokenRequest = authAPI.requestToken(identityProviderConfiguration.getUrl());
TokenHolder holder = tokenRequest.execute().getBody();
return holder.getAccessToken();

}

@Override
public String getServiceName() {
return SUNBIRD_AUTH0_SERVICE_NAME;
}

@Override
public ComponentHealthInfo getHealthInfo() {
if (identityProviderConfiguration.isAuthenticationEnabled()) {
try {
//TODO: check auth0 status
return new ComponentHealthInfo(getServiceName(), true);
} catch (Exception e) {
return new ComponentHealthInfo(getServiceName(), false, CONNECTION_FAILURE, e.getMessage());
}
} else {
return new ComponentHealthInfo(getServiceName(), true, "AUTHENTICATION_ENABLED", "false");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.sunbirdrc.auth.auth0;

import dev.sunbirdrc.registry.identity_providers.pojos.IdentityManager;
import dev.sunbirdrc.registry.identity_providers.pojos.IdentityProviderConfiguration;
import dev.sunbirdrc.registry.identity_providers.providers.IdentityProvider;

public class Auth0ProviderImpl implements IdentityProvider {
@Override
public IdentityManager createManager(IdentityProviderConfiguration identityProviderConfiguration) {
return new Auth0AdminUtil(identityProviderConfiguration);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.sunbirdrc.auth.auth0.Auth0ProviderImpl
Loading