-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6185bed
commit 145dae1
Showing
16 changed files
with
971 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<?xml version="1.0"?> | ||
<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> | ||
|
||
<groupId>io.jans.casa.plugins</groupId> | ||
<artifactId>${plugin.id}</artifactId> | ||
<version>1.1.0-SNAPSHOT</version> | ||
<packaging>jar</packaging> | ||
|
||
<properties> | ||
<maven.compiler.source>11</maven.compiler.source> | ||
<maven.compiler.target>11</maven.compiler.target> | ||
<plugin.id>acct-linking</plugin.id> | ||
</properties> | ||
|
||
<repositories> | ||
<repository> | ||
<id>jans</id> | ||
<name>Janssen project repository</name> | ||
<url>https://maven.jans.io/maven</url> | ||
</repository> | ||
</repositories> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<version>3.1.0</version> | ||
<configuration> | ||
<descriptorRefs> | ||
<descriptorRef>jar-with-dependencies</descriptorRef> | ||
</descriptorRefs> | ||
<archive> | ||
<manifestEntries> | ||
<Plugin-Id>${plugin.id}</Plugin-Id> | ||
<Plugin-Version>${project.version}</Plugin-Version> | ||
<Plugin-Provider>Janssen project</Plugin-Provider> | ||
<Plugin-Class>io.jans.casa.plugins.acctlinking.AccountsLinkingPlugin</Plugin-Class> | ||
<Plugin-Description> | ||
Allows the user to link their external identities (social sites and other OAuth providers) | ||
to his local Jans account | ||
</Plugin-Description> | ||
<Plugin-License>Available under Apache 2 license</Plugin-License> | ||
<Logger-Name>io.jans.casa.plugins</Logger-Name> | ||
</manifestEntries> | ||
</archive> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<id>make-assembly</id> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>com.github.spotbugs</groupId> | ||
<artifactId>spotbugs-maven-plugin</artifactId> | ||
<version>4.2.0</version> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<dependencies> | ||
|
||
<dependency> | ||
<groupId>com.nimbusds</groupId> | ||
<artifactId>oauth2-oidc-sdk</artifactId> | ||
<version>11.7</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>io.jans</groupId> | ||
<artifactId>agama-inbound</artifactId> | ||
<version>${project.version}</version> | ||
<exclusions> | ||
<exclusion> | ||
<groupId>*</groupId> | ||
<artifactId>*</artifactId> | ||
</exclusion> | ||
</exclusions> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>io.jans</groupId> | ||
<artifactId>jans-core-cache</artifactId> | ||
<version>${project.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.jans</groupId> | ||
<artifactId>casa-shared</artifactId> | ||
<version>${project.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.jans</groupId> | ||
<artifactId>casa-config</artifactId> | ||
<version>${project.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
24 changes: 24 additions & 0 deletions
24
...ns/acct-linking/src/main/java/io/jans/casa/plugins/acctlinking/AccountsLinkingPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.jans.casa.plugins.acctlinking; | ||
|
||
import org.pf4j.Plugin; | ||
import org.pf4j.PluginWrapper; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class AccountsLinkingPlugin extends Plugin { | ||
|
||
private Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
public AccountsLinkingPlugin(PluginWrapper wrapper) throws Exception { | ||
super(wrapper); | ||
} | ||
|
||
@Override | ||
public void start() { | ||
} | ||
|
||
@Override | ||
public void delete() { | ||
} | ||
|
||
} |
196 changes: 196 additions & 0 deletions
196
...s/acct-linking/src/main/java/io/jans/casa/plugins/acctlinking/AccountsLinkingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package io.jans.casa.plugins.acctlinking; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.nimbusds.oauth2.sdk.http.HTTPRequest; | ||
import com.nimbusds.oauth2.sdk.http.HTTPResponse; | ||
import com.nimbusds.oauth2.sdk.ParseException; | ||
|
||
import io.jans.casa.conf.OIDCClientSettings; | ||
import io.jans.casa.core.model.IdentityPerson; | ||
import io.jans.casa.misc.Utils; | ||
import io.jans.casa.model.ApplicationConfiguration; | ||
import io.jans.casa.service.IPersistenceService; | ||
import io.jans.inbound.Provider; | ||
|
||
import java.io.IOException; | ||
import java.net.*; | ||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
||
public class AccountsLinkingService { | ||
|
||
public static final String CASA_AGAMA_FLOW = "io.jans.casa.acctlinking.Launcher"; | ||
|
||
public static final String READ_SCOPE = "https://jans.io/oauth/config/agama.readonly"; | ||
|
||
private static final String AGAMA_PRJ = "casa-account-linking"; | ||
|
||
private static final String CONFIGS_ENDPOINT = | ||
"/jans-config-api/api/v1/agama-deployment/configs/" + AGAMA_PRJ; | ||
|
||
private Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
private static AccountsLinkingService instance; | ||
private IPersistenceService ips; | ||
private ObjectMapper mapper; | ||
|
||
private OIDCClientSettings clSettings; | ||
private String issuer; | ||
private String basicAuthnHeader; | ||
|
||
public static AccountsLinkingService getInstance() { | ||
if (instance == null) { | ||
instance = new AccountsLinkingService(); | ||
} | ||
return instance; | ||
} | ||
|
||
public OIDCClientSettings getCasaClient() { | ||
return clSettings; | ||
} | ||
|
||
public Map<String, Provider> getProviders(boolean enabledOnly) throws Exception { | ||
|
||
HTTPRequest request = new HTTPRequest(HTTPRequest.Method.GET, | ||
new URL(issuer + CONFIGS_ENDPOINT)); | ||
setTimeouts(request); | ||
request.setAuthorization(basicAuthnHeader); | ||
request.setAuthorization("Bearer " + getAToken()); | ||
|
||
HTTPResponse r = request.send(); | ||
r.ensureStatusCode(200); | ||
|
||
Map<String, Map<String, Provider>> madam = mapper.readValue( | ||
r.getBody(), new TypeReference<Map<String, Map<String, Provider>>>(){}); | ||
|
||
Map<String, Provider> madman = Optional.ofNullable(madam) | ||
.map(m -> m.get(CASA_AGAMA_FLOW)).orElse(Collections.emptyMap()); | ||
|
||
if (enabledOnly) { | ||
madman = madman.entrySet().stream().filter(e -> e.getValue().isEnabled()) | ||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
} | ||
return madman; | ||
|
||
} | ||
|
||
public Map<String, String> getAccounts(String userId, Set<String> knownProviders) { | ||
|
||
Map<String, String> accts = new HashMap<>(); | ||
for (String extUid : getPerson(userId).getJansExtUid()) { | ||
//See method computeExtUid | ||
int i = extUid.indexOf(":"); | ||
|
||
if (i > 0 && i < extUid.length() - 1) { | ||
String pref = extUid.substring(0, i); | ||
if (knownProviders.contains(pref)) { | ||
accts.put(pref, extUid.substring(i + 1)); | ||
} | ||
} | ||
} | ||
return accts; | ||
|
||
} | ||
|
||
/* | ||
//linking occurs at the Agama flow | ||
public boolean link(String userId, String providerId, String extUid) { | ||
try { | ||
IdentityPerson p = getPerson(userId); | ||
List<String> extUids = new ArrayList<>(p.getJansExtUid()); | ||
extUids.add(computeExtUid(providerId, extUid)); | ||
p.setJansExtUid(extUids); | ||
ips.modify(p); | ||
return true; | ||
} catch (Exception e) { | ||
logger.error(e.getMessage(), e); | ||
} | ||
return false; | ||
}*/ | ||
|
||
public boolean delink(String userId, String providerId, String extUid) { | ||
|
||
try { | ||
IdentityPerson p = getPerson(userId); | ||
List<String> extUids = new ArrayList<>(p.getJansExtUid()); | ||
extUids.remove(computeExtUid(providerId, extUid)); | ||
|
||
p.setJansExtUid(extUids); | ||
ips.modify(p); | ||
return true; | ||
} catch (Exception e) { | ||
logger.error(e.getMessage(), e); | ||
} | ||
return false; | ||
|
||
} | ||
|
||
public boolean hasPassword(String id) { | ||
return getPerson(id).hasPassword(); | ||
} | ||
|
||
private IdentityPerson getPerson(String id) { | ||
return ips.get(IdentityPerson.class, ips.getPersonDn(id)); | ||
} | ||
|
||
private String computeExtUid(String providerId, String id) { | ||
//This method HAS to match computeExtUid in class io.jans.agama.inbound.Utils | ||
return providerId + ":" + id; | ||
} | ||
|
||
private AccountsLinkingService() { | ||
logger.info("Initializing AccountsLinkingService"); | ||
mapper = new ObjectMapper(); | ||
|
||
ips = Utils.managedBean(IPersistenceService.class); | ||
issuer = ips.getIssuerUrl(); | ||
logger.debug("Issuer is {}", issuer); | ||
|
||
clSettings = ips.get(ApplicationConfiguration.class, "ou=casa,ou=configuration,o=jans") | ||
.getSettings().getOidcSettings().getClient(); | ||
|
||
String authz = clSettings.getClientId() + ":" + clSettings.getClientSecret(); | ||
authz = new String(Base64.getEncoder().encode(authz.getBytes(UTF_8)), UTF_8); | ||
basicAuthnHeader = "Basic " + authz; | ||
|
||
} | ||
|
||
private String getAToken() throws IOException { | ||
|
||
StringJoiner joiner = new StringJoiner("&"); | ||
Map.of("grant_type", "client_credentials", "scope", URLEncoder.encode(READ_SCOPE, UTF_8)) | ||
.forEach((k, v) -> joiner.add(k + "=" + v)); | ||
|
||
logger.info("Calling token endpoint"); | ||
|
||
HTTPRequest request = new HTTPRequest( | ||
HTTPRequest.Method.POST, new URL(issuer + "/jans-auth/restv1/token")); | ||
setTimeouts(request); | ||
request.setQuery(joiner.toString()); | ||
request.setAuthorization(basicAuthnHeader); | ||
|
||
try { | ||
Map<String, Object> jobj = request.send().getContentAsJSONObject(); | ||
logger.info("Successful call"); | ||
return jobj.get("access_token").toString(); | ||
} catch (Exception e) { | ||
throw new IOException(e.getMessage(), e); | ||
} | ||
|
||
} | ||
|
||
private void setTimeouts(HTTPRequest request) { | ||
request.setConnectTimeout(3500); | ||
request.setReadTimeout(3500); | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
...gins/acct-linking/src/main/java/io/jans/casa/plugins/acctlinking/extension/AdminMenu.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.jans.casa.plugins.acctlinking; | ||
|
||
import io.jans.casa.extension.navigation.MenuType; | ||
import io.jans.casa.extension.navigation.NavigationMenu; | ||
import org.pf4j.Extension; | ||
|
||
@Extension | ||
public class AdminMenu implements NavigationMenu { | ||
|
||
public String getContentsUrl() { | ||
return "admin/menu.zul"; | ||
} | ||
|
||
public MenuType menuType() { | ||
return MenuType.ADMIN_CONSOLE; | ||
} | ||
|
||
public float getPriority() { | ||
return 0.1f; | ||
} | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
...ugins/acct-linking/src/main/java/io/jans/casa/plugins/acctlinking/extension/UserMenu.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.jans.casa.plugins.acctlinking; | ||
|
||
import io.jans.casa.extension.navigation.MenuType; | ||
import io.jans.casa.extension.navigation.NavigationMenu; | ||
import org.pf4j.Extension; | ||
|
||
@Extension | ||
public class UserMenu implements NavigationMenu { | ||
|
||
public String getContentsUrl() { | ||
return "user/menu.zul"; | ||
} | ||
|
||
public MenuType menuType() { | ||
return MenuType.USER; | ||
} | ||
|
||
public float getPriority() { | ||
return 0.1f; | ||
} | ||
|
||
} | ||
|
Oops, something went wrong.