Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pathob committed Mar 21, 2024
1 parent 8fab756 commit 7b2fd68
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 1 deletion.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>confapi-jira-plugin</artifactId>
<version>0.0.7-SNAPSHOT</version>
<version>0.1.0-SNAPSHOT</version>
<packaging>atlassian-plugin</packaging>

<name>ConfAPI for Jira</name>
Expand Down Expand Up @@ -221,6 +221,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.atlassian.plugins.authentication</groupId>
<artifactId>atlassian-authentication-plugin</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package de.aservo.confapi.jira.model.util;

import com.atlassian.plugins.authentication.api.config.IdpConfig;
import com.atlassian.plugins.authentication.api.config.SsoType;
import com.atlassian.plugins.authentication.api.config.oidc.OidcConfig;
import com.atlassian.plugins.authentication.api.config.saml.SamlConfig;
import de.aservo.confapi.commons.exception.BadRequestException;
import de.aservo.confapi.commons.exception.InternalServerErrorException;
import de.aservo.confapi.commons.model.AbstractAuthenticationIdpBean;
import de.aservo.confapi.commons.model.AuthenticationIdpOidcBean;
import de.aservo.confapi.commons.model.AuthenticationIdpSamlBean;

public class AuthenticationIdpBeanUtil {

public static IdpConfig toIdpConfig(
final AbstractAuthenticationIdpBean authenticationIdpBean) {

return toIdpConfig(authenticationIdpBean, null);
}

public static IdpConfig toIdpConfig(
final AbstractAuthenticationIdpBean authenticationIdpBean,
final IdpConfig existingIdpConfig) {

if (authenticationIdpBean instanceof AuthenticationIdpOidcBean) {
return toOidcConfig((AuthenticationIdpOidcBean) authenticationIdpBean, existingIdpConfig);
}

throw new BadRequestException("IDP types other than OIDC are not (yet) supported");
}

private static OidcConfig toOidcConfig(
final AuthenticationIdpOidcBean authenticationIdpOidcBean,
final IdpConfig existingIdpConfig) {

final OidcConfig.Builder oidcConfigBuilder;

if (existingIdpConfig == null) {
oidcConfigBuilder = OidcConfig.builder();
} else {
verifyIdAndType(authenticationIdpOidcBean, existingIdpConfig, OidcConfig.class);
oidcConfigBuilder = OidcConfig.builder((OidcConfig) existingIdpConfig);
}

if (authenticationIdpOidcBean.getId() != null) {
oidcConfigBuilder.setId(authenticationIdpOidcBean.getId());
}
if (authenticationIdpOidcBean.getName() != null) {
oidcConfigBuilder.setName(authenticationIdpOidcBean.getName());
}
if (authenticationIdpOidcBean.getEnabled() != null) {
oidcConfigBuilder.setEnabled(authenticationIdpOidcBean.getEnabled());
}
if (authenticationIdpOidcBean.getUrl() != null) {
oidcConfigBuilder.setIssuer(authenticationIdpOidcBean.getUrl());
}
if (authenticationIdpOidcBean.getEnableRememberMe() != null) {
oidcConfigBuilder.setEnableRememberMe(authenticationIdpOidcBean.getEnableRememberMe());
}
if (authenticationIdpOidcBean.getButtonText() != null) {
oidcConfigBuilder.setButtonText(authenticationIdpOidcBean.getButtonText());
}
if (authenticationIdpOidcBean.getClientId() != null) {
oidcConfigBuilder.setClientId(authenticationIdpOidcBean.getClientId());
}
if (authenticationIdpOidcBean.getClientSecret() != null) {
oidcConfigBuilder.setClientSecret(authenticationIdpOidcBean.getClientSecret());
}
if (authenticationIdpOidcBean.getUsernameClaim() != null) {
oidcConfigBuilder.setUsernameClaim(authenticationIdpOidcBean.getUsernameClaim());
}
if (authenticationIdpOidcBean.getAdditionalScopes() != null) {
oidcConfigBuilder.setAdditionalScopes(authenticationIdpOidcBean.getAdditionalScopes());
}
if (authenticationIdpOidcBean.getDiscoveryEnabled() != null) {
oidcConfigBuilder.setDiscoveryEnabled(authenticationIdpOidcBean.getDiscoveryEnabled());
}
if (authenticationIdpOidcBean.getAuthorizationEndpoint() != null) {
oidcConfigBuilder.setAuthorizationEndpoint(authenticationIdpOidcBean.getAuthorizationEndpoint());
}
if (authenticationIdpOidcBean.getTokenEndpoint() != null) {
oidcConfigBuilder.setTokenEndpoint(authenticationIdpOidcBean.getTokenEndpoint());
}
if (authenticationIdpOidcBean.getUserInfoEndpoint() != null) {
oidcConfigBuilder.setUserInfoEndpoint(authenticationIdpOidcBean.getUserInfoEndpoint());
}

return oidcConfigBuilder.build();
}

public static AbstractAuthenticationIdpBean toAuthenticationIdpBean(
final IdpConfig idpConfig) {

if (idpConfig.getSsoType().equals(SsoType.OIDC)) {
return toAuthenticationIdpOidcBean(idpConfig);
} else if (idpConfig.getSsoType().equals(SsoType.SAML)) {
return toAuthenticationIdpSamlBean(idpConfig);
}

throw new UnsupportedOperationException("The IDP type cannot be NONE");
}

private static AuthenticationIdpOidcBean toAuthenticationIdpOidcBean(
final IdpConfig idpConfig) {

if (!(idpConfig instanceof OidcConfig)) {
throw new InternalServerErrorException("The class of the IDP config is not OIDC");
}

final OidcConfig oidcConfig = (OidcConfig) idpConfig;

final AuthenticationIdpOidcBean authenticationIdpOidcBean = new AuthenticationIdpOidcBean();
authenticationIdpOidcBean.setId(oidcConfig.getId());
authenticationIdpOidcBean.setName(oidcConfig.getName());
authenticationIdpOidcBean.setEnabled(oidcConfig.isEnabled());
authenticationIdpOidcBean.setUrl(oidcConfig.getIssuer());
authenticationIdpOidcBean.setEnableRememberMe(oidcConfig.isEnableRememberMe());
authenticationIdpOidcBean.setButtonText(oidcConfig.getButtonText());
authenticationIdpOidcBean.setClientId(oidcConfig.getClientId());
authenticationIdpOidcBean.setUsernameClaim(oidcConfig.getUsernameClaim());
authenticationIdpOidcBean.setAdditionalScopes(oidcConfig.getAdditionalScopes());
authenticationIdpOidcBean.setDiscoveryEnabled(oidcConfig.isDiscoveryEnabled());
authenticationIdpOidcBean.setAuthorizationEndpoint(oidcConfig.getAuthorizationEndpoint());
authenticationIdpOidcBean.setTokenEndpoint(oidcConfig.getTokenEndpoint());
authenticationIdpOidcBean.setUserInfoEndpoint(oidcConfig.getUserInfoEndpoint());

return authenticationIdpOidcBean;
}

private static AuthenticationIdpSamlBean toAuthenticationIdpSamlBean(
final IdpConfig idpConfig) {

if (!(idpConfig instanceof SamlConfig)) {
throw new InternalServerErrorException("The class of the IDP config is not SAML");
}

final SamlConfig samlConfig = (SamlConfig) idpConfig;

final AuthenticationIdpSamlBean authenticationIdpSamlBean = new AuthenticationIdpSamlBean();
authenticationIdpSamlBean.setId(samlConfig.getId());
authenticationIdpSamlBean.setName(samlConfig.getName());
authenticationIdpSamlBean.setEnabled(samlConfig.isEnabled());
authenticationIdpSamlBean.setUrl(samlConfig.getIssuer());
authenticationIdpSamlBean.setEnableRememberMe(samlConfig.isEnableRememberMe());
authenticationIdpSamlBean.setButtonText(samlConfig.getButtonText());
// is it wanted to return the certificate here?
authenticationIdpSamlBean.setUsernameAttribute(samlConfig.getUsernameAttribute());

return authenticationIdpSamlBean;
}

private static void verifyIdAndType(
final AbstractAuthenticationIdpBean authenticationIdpBean,
final IdpConfig existingIdpConfig,
final Class<? extends IdpConfig> clazz) {

if (authenticationIdpBean.getId() != null && !authenticationIdpBean.getId().equals(existingIdpConfig.getId())) {
throw new BadRequestException("An ID has been passed but it does not match the ID of the existing IDP with the same name");
}

if (!clazz.isAssignableFrom(existingIdpConfig.getClass())) {
throw new BadRequestException("The existing IDP config with the same name is not of type OIDC");
}
}

private AuthenticationIdpBeanUtil() {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package de.aservo.confapi.jira.model.util;

import com.atlassian.plugins.authentication.api.config.ImmutableSsoConfig;
import com.atlassian.plugins.authentication.api.config.SsoConfig;
import de.aservo.confapi.commons.model.AuthenticationSsoBean;

public class AuthenticationSsoBeanUtil {

public static SsoConfig toSsoConfig(
final AuthenticationSsoBean authenticationSsoBean) {

return toSsoConfig(authenticationSsoBean, null);
}

public static SsoConfig toSsoConfig(
final AuthenticationSsoBean authenticationSsoBean,
final SsoConfig existingSsoConfig) {

final ImmutableSsoConfig.Builder ssoConfigBuilder;

if (existingSsoConfig != null) {
ssoConfigBuilder = ImmutableSsoConfig.toBuilder(existingSsoConfig);
} else {
ssoConfigBuilder = ImmutableSsoConfig.builder();
}

if (authenticationSsoBean.getShowOnLogin() != null) {
ssoConfigBuilder.setShowLoginForm(authenticationSsoBean.getShowOnLogin());
}

return ssoConfigBuilder.build();
}

public static AuthenticationSsoBean toAuthenticationSsoBean(
final SsoConfig ssoConfig) {

final AuthenticationSsoBean authenticationSsoBean = new AuthenticationSsoBean();
authenticationSsoBean.setShowOnLogin(ssoConfig.getShowLoginForm());

return authenticationSsoBean;
}

private AuthenticationSsoBeanUtil() {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.aservo.confapi.jira.rest;

import com.sun.jersey.spi.container.ResourceFilters;
import de.aservo.confapi.commons.constants.ConfAPI;
import de.aservo.confapi.commons.rest.AbstractAuthenticationResourceImpl;
import de.aservo.confapi.commons.service.api.AuthenticationService;
import de.aservo.confapi.jira.filter.SysadminOnlyResourceFilter;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import javax.ws.rs.Path;

@Path(ConfAPI.AUTHENTICATION)
@ResourceFilters(SysadminOnlyResourceFilter.class)
@Component
public class AuthenticationResourceImpl extends AbstractAuthenticationResourceImpl {

@Inject
public AuthenticationResourceImpl(AuthenticationService authenticationService) {
super(authenticationService);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package de.aservo.confapi.jira.service;

import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.plugins.authentication.api.config.IdpConfig;
import com.atlassian.plugins.authentication.api.config.IdpConfigService;
import com.atlassian.plugins.authentication.api.config.SsoConfig;
import com.atlassian.plugins.authentication.api.config.SsoConfigService;
import de.aservo.confapi.commons.exception.BadRequestException;
import de.aservo.confapi.commons.model.AbstractAuthenticationIdpBean;
import de.aservo.confapi.commons.model.AuthenticationIdpsBean;
import de.aservo.confapi.commons.model.AuthenticationSsoBean;
import de.aservo.confapi.commons.service.api.AuthenticationService;
import de.aservo.confapi.jira.model.util.AuthenticationIdpBeanUtil;
import de.aservo.confapi.jira.model.util.AuthenticationSsoBeanUtil;
import org.springframework.stereotype.Component;

import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@ExportAsService(AuthenticationService.class)
public class AuthenticationServiceImpl implements AuthenticationService {

@ComponentImport
private final IdpConfigService idpConfigService;

@ComponentImport
private final SsoConfigService ssoConfigService;

public AuthenticationServiceImpl(
final IdpConfigService idpConfigService,
final SsoConfigService ssoConfigService) {

this.idpConfigService = idpConfigService;
this.ssoConfigService = ssoConfigService;
}

@Override
public AuthenticationIdpsBean getAuthenticationIdps() {
return new AuthenticationIdpsBean(idpConfigService.getIdpConfigs().stream()
.map(AuthenticationIdpBeanUtil::toAuthenticationIdpBean)
.sorted(authenticationIdpBeanComparator)
.collect(Collectors.toList()));
}

@Override
public AuthenticationIdpsBean setAuthenticationIdps(
final AuthenticationIdpsBean authenticationIdpsBean) {

return new AuthenticationIdpsBean(authenticationIdpsBean.getAuthenticationIdpBeans().stream()
.map(this::setAuthenticationIdp)
.sorted(authenticationIdpBeanComparator)
.collect(Collectors.toList()));
}

public AbstractAuthenticationIdpBean setAuthenticationIdp(
final AbstractAuthenticationIdpBean authenticationIdpBean) {

if (authenticationIdpBean.getName() == null || authenticationIdpBean.getName().trim().isEmpty()) {
throw new BadRequestException("The name cannot be empty");
}

final IdpConfig existingIdpConfig = findIdpConfigByName(authenticationIdpBean.getName());

if (existingIdpConfig == null) {
final IdpConfig idpConfig = AuthenticationIdpBeanUtil.toIdpConfig(authenticationIdpBean);
final IdpConfig addedIdpConfig = idpConfigService.addIdpConfig(idpConfig);
return AuthenticationIdpBeanUtil.toAuthenticationIdpBean(addedIdpConfig);
}

final IdpConfig idpConfig = AuthenticationIdpBeanUtil.toIdpConfig(authenticationIdpBean, existingIdpConfig);
final IdpConfig updatedIdpConfig = idpConfigService.updateIdpConfig(idpConfig);
return AuthenticationIdpBeanUtil.toAuthenticationIdpBean(updatedIdpConfig);
}

@Override
public AuthenticationSsoBean getAuthenticationSso() {
return AuthenticationSsoBeanUtil.toAuthenticationSsoBean(ssoConfigService.getSsoConfig());
}

@Override
public AuthenticationSsoBean setAuthenticationSso(AuthenticationSsoBean authenticationSsoBean) {
final SsoConfig existingSsoConfig = ssoConfigService.getSsoConfig();
final SsoConfig ssoConfig = AuthenticationSsoBeanUtil.toSsoConfig(authenticationSsoBean, existingSsoConfig);
return AuthenticationSsoBeanUtil.toAuthenticationSsoBean(ssoConfigService.updateSsoConfig(ssoConfig));
}

IdpConfig findIdpConfigByName(
final String name) {

final Map<String, IdpConfig> idpConfigsByName = idpConfigService.getIdpConfigs().stream().collect(Collectors.toMap(
IdpConfig::getName, Function.identity(), (existing, replacement) -> {
throw new IllegalStateException("Duplicate name key found: " + existing.getName());
}
));

return idpConfigsByName.get(name);
}

static Comparator<AbstractAuthenticationIdpBean> authenticationIdpBeanComparator = (a1, a2) -> a1.getName().compareToIgnoreCase(a2.getName());

}
Loading

0 comments on commit 7b2fd68

Please sign in to comment.