Repository: EnMasseProject/enmasse
Pull request #2675 (2019-04-16 09:24:05)

Allow overriding authentication service details per address space for the external type
Also clean up some unused code.


Patch:
diff --git a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java b/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
index 95b99f8ac..8613a1cd4 100644
--- a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
+++ b/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
@@ -6,17 +6,21 @@ package io.enmasse.controller;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import io.enmasse.address.model.*;
+import io.enmasse.address.model.AddressSpace;
+import io.enmasse.address.model.CertSpec;
+import io.enmasse.address.model.EndpointSpec;
+import io.enmasse.address.model.KubeUtil;
 import io.enmasse.admin.model.v1.*;
-import io.enmasse.admin.model.v1.AuthenticationService;
-import io.enmasse.admin.model.v1.AuthenticationServiceType;
 import io.enmasse.config.AnnotationKeys;
 import io.enmasse.config.LabelKeys;
 import io.enmasse.controller.common.Kubernetes;
 import io.enmasse.controller.common.TemplateParameter;
 import io.enmasse.k8s.api.AuthenticationServiceRegistry;
 import io.enmasse.k8s.api.SchemaProvider;
-import io.fabric8.kubernetes.api.model.*;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PodTemplateSpec;
+import io.fabric8.kubernetes.api.model.SecretReference;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.fabric8.kubernetes.api.model.apps.StatefulSet;
 import org.slf4j.Logger;
@@ -25,8 +29,6 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.*;
 
@@ -63,6 +65,22 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
             throw new IllegalArgumentException("Authentication service '" + authService.getMetadata().getName() + "' is not yet deployed");
         }
 
+        String authServiceHost = authService.getStatus().getHost();
+        int authServicePort = authService.getStatus().getPort();
+        String authServiceRealm = authService.getSpec().getRealm() != null ? authService.getSpec().getRealm() : addressSpace.getAnnotation(AnnotationKeys.REALM_NAME);
+
+        if (authService.getSpec().getType().equals(AuthenticationServiceType.external) && authService.getSpec().getExternal() != null && authService.getSpec().getExternal().isAllowOverride()) {
+            if (addressSpace.getSpec().getAuthenticationService().getHost() != null) {
+                authServiceHost = addressSpace.getSpec().getAuthenticationService().getHost();
+            }
+            if (addressSpace.getSpec().getAuthenticationService().getPort() != null) {
+                authServicePort = addressSpace.getSpec().getAuthenticationService().getPort();
+            }
+            if (addressSpace.getSpec().getAuthenticationService().getRealm() != null) {
+                authServiceRealm = addressSpace.getSpec().getAuthenticationService().getRealm();
+            }
+        }
+
         Optional<ConsoleService> console = schemaProvider.getSchema().findConsoleService(WELL_KNOWN_CONSOLE_SERVICE_NAME);
         if (console.isEmpty()) {
             log.warn("No ConsoleService found named '{}', address space console service will be unavailable", WELL_KNOWN_CONSOLE_SERVICE_NAME);
@@ -73,8 +91,8 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
         parameters.put(TemplateParameter.ADDRESS_SPACE, addressSpace.getMetadata().getName());
         parameters.put(TemplateParameter.INFRA_UUID, infraUuid);
         parameters.put(TemplateParameter.ADDRESS_SPACE_NAMESPACE, addressSpace.getMetadata().getNamespace());
-        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, authService.getStatus().getHost());
-        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, String.valueOf(authService.getStatus().getPort()));
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, authServiceHost);
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, String.valueOf(authServicePort));
         parameters.put(TemplateParameter.ADDRESS_SPACE_PLAN, addressSpace.getSpec().getPlan());
 
         String encodedCaCert = Optional.ofNullable(authService.getStatus().getCaCertSecret())
@@ -94,11 +112,7 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
             parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_CLIENT_SECRET, authService.getStatus().getClientCertSecret().getName());
         }
 
-        if (authService.getSpec().getRealm() != null) {
-            parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, authService.getSpec().getRealm());
-        } else {
-            parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, addressSpace.getAnnotation(AnnotationKeys.REALM_NAME));
-        }
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, authServiceRealm);
 
         Map<String, CertSpec> serviceCertMapping = new HashMap<>();
         for (EndpointSpec endpoint : addressSpace.getSpec().getEndpoints()) {
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
index ca421f199..a51de0ef1 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
@@ -4,21 +4,15 @@
  */
 package io.enmasse.address.model;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
 import com.fasterxml.jackson.annotation.JsonInclude;
-
 import io.enmasse.admin.model.v1.AbstractWithAdditionalProperties;
-import io.enmasse.common.model.AbstractHasMetadata;
-import io.enmasse.model.validation.AuthenticationServiceDetails;
 import io.fabric8.kubernetes.api.model.Doneable;
 import io.sundr.builder.annotations.Buildable;
 import io.sundr.builder.annotations.BuildableReference;
 import io.sundr.builder.annotations.Inline;
 
+import java.util.Objects;
+
 /**
  * Represents an authentication service for an {@link AddressSpace}.
  */
@@ -34,12 +28,16 @@ import io.sundr.builder.annotations.Inline;
                 )
         )
 @JsonInclude(JsonInclude.Include.NON_NULL)
-@AuthenticationServiceDetails
 public class AuthenticationService extends AbstractWithAdditionalProperties {
 
     private String name;
+
+    private String host;
+    private Integer port;
+    private String realm;
+
+    // TODO: Keep for backwards compatibility
     private AuthenticationServiceType type;
-    private Map<String, Object> details = new HashMap<> ();
 
     public AuthenticationService() {
     }
@@ -52,14 +50,6 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         return type;
     }
 
-    public void setDetails(Map<String, Object> details) {
-        this.details = details;
-    }
-
-    public Map<String, Object> getDetails() {
-        return Collections.unmodifiableMap(details);
-    }
-
     public String getName() {
         return name;
     }
@@ -68,6 +58,30 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         this.name = name;
     }
 
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public Integer getPort() {
+        return port;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+    }
+
+    public String getRealm() {
+        return realm;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -75,11 +89,13 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         final AuthenticationService that = (AuthenticationService) o;
         return Objects.equals(name, that.name) &&
                 Objects.equals(type, that.type) &&
-                Objects.equals(details, that.details);
+                Objects.equals(host, that.host) &&
+                Objects.equals(port, that.port) &&
+                Objects.equals(realm, that.realm);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, type, details);
+        return Objects.hash(name, type, host, port, realm);
     }
 }
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java
deleted file mode 100644
index fe2c45783..000000000
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017-2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-package io.enmasse.address.model;
-
-public class AuthenticationServiceDetail {
-    private final String name;
-    private final Class<?> type;
-    private final boolean mandatory;
-
-    public AuthenticationServiceDetail(String name, Class<?> type, boolean mandatory) {
-        this.name = name;
-        this.type = type;
-        this.mandatory = mandatory;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Class<?> getType() {
-        return type;
-    }
-
-    public boolean isMandatory() {
-        return mandatory;
-    }
-}
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
index 71450642a..b80538ada 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
@@ -4,8 +4,6 @@
  */
 package io.enmasse.address.model;
 
-import java.util.*;
-
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonValue;
 
@@ -15,42 +13,17 @@ import com.fasterxml.jackson.annotation.JsonValue;
 public enum AuthenticationServiceType {
     NONE,
     STANDARD,
-    EXTERNAL(new AuthenticationServiceDetail("host", String.class, true),
-        new AuthenticationServiceDetail("port", Integer.class, true),
-            new AuthenticationServiceDetail("caCertSecretName", String.class, false),
-            new AuthenticationServiceDetail("clientCertSecretName", String.class, false),
-            new AuthenticationServiceDetail("saslInitHost", String.class, true));
-
-    private final Map<String, Class<?>> detailsFields = new HashMap<>();
-    private final Set<String> mandatoryFields = new HashSet<>();
+    EXTERNAL;
 
-    AuthenticationServiceType(AuthenticationServiceDetail ... details) {
-        for (AuthenticationServiceDetail detail : details) {
-            detailsFields.put(detail.getName(), detail.getType());
-
-            if (detail.isMandatory()) {
-                mandatoryFields.add(detail.getName());
-            }
-        }
+    @JsonValue
+    public String getName() {
+        return name().toLowerCase();
     }
 
     public io.enmasse.admin.model.v1.AuthenticationServiceType toAdminType() {
         return io.enmasse.admin.model.v1.AuthenticationServiceType.valueOf(name().toLowerCase());
     }
 
-    public Map<String, Class<?>> getDetailsFields() {
-        return Collections.unmodifiableMap(detailsFields);
-    }
-
-    public Set<String> getMandatoryFields() {
-        return Collections.unmodifiableSet(mandatoryFields);
-    }
-
-    @JsonValue
-    public String getName() {
-        return name().toLowerCase();
-    }
-
     @JsonCreator
     public static AuthenticationServiceType create(String name) {
         return valueOf(name.toUpperCase());
diff --git a/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java b/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
index 8c8be5b7d..5c50c2fa5 100644
--- a/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
+++ b/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
@@ -11,6 +11,7 @@ import java.util.Objects;
 public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalProperties {
     private String host;
     private int port;
+    private boolean allowOverride = false;
     private SecretReference caCertSecret;
     private SecretReference clientCertSecret;
 
@@ -53,13 +54,14 @@ public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalPro
         AuthenticationServiceSpecExternal that = (AuthenticationServiceSpecExternal) o;
         return port == that.port &&
                 Objects.equals(host, that.host) &&
+                Objects.equals(allowOverride, that.allowOverride) &&
                 Objects.equals(caCertSecret, that.caCertSecret) &&
                 Objects.equals(clientCertSecret, that.clientCertSecret);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(host, port, caCertSecret, clientCertSecret);
+        return Objects.hash(host, port, allowOverride, caCertSecret, clientCertSecret);
     }
 
     @Override
@@ -67,8 +69,17 @@ public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalPro
         return "AuthenticationServiceSpecExternal{" +
                 "host='" + host + '\'' +
                 ", port=" + port +
+                ", allowOverride=" + allowOverride +
                 ", caCertSecret=" + caCertSecret +
                 ", clientCertSecret=" + clientCertSecret +
                 '}';
     }
+
+    public boolean isAllowOverride() {
+        return allowOverride;
+    }
+
+    public void setAllowOverride(boolean allowOverride) {
+        this.allowOverride = allowOverride;
+    }
 }
diff --git a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java b/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java
deleted file mode 100644
index 14c9cd57b..000000000
--- a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.model.validation;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-
-@Target({TYPE, ANNOTATION_TYPE})
-@Retention(RUNTIME)
-@Constraint(validatedBy = {AuthenticationServiceDetailsValidator.class})
-@Documented
-public @interface AuthenticationServiceDetails {
-
-    String message() default "Invalid authentication service details";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-
-}
diff --git a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java b/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java
deleted file mode 100644
index bab64c88b..000000000
--- a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.model.validation;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-
-import io.enmasse.address.model.AuthenticationService;
-import io.enmasse.address.model.AuthenticationServiceType;
-
-public class AuthenticationServiceDetailsValidator implements ConstraintValidator<AuthenticationServiceDetails, AuthenticationService> {
-
-    @Override
-    public boolean isValid(final AuthenticationService service, final ConstraintValidatorContext context) {
-
-        if (service == null) {
-            // nothing to validate
-            return true;
-        }
-
-        if (service.getType() == null) {
-            // nothing to validate
-            return true;
-        }
-
-        Map<String, Object> details = service.getDetails();
-        if (details == null) {
-            // null map means: no values -> empty map
-            // which may be fine for some services
-            details = Collections.emptyMap();
-        }
-
-        final Set<String> missing = findMissing(service.getType(), service.getDetails());
-        final Set<String> unknown = findUnknown(service.getType(), service.getDetails());
-
-        handleFields(context, "Missing", missing);
-        handleFields(context, "Unknown", unknown);
-
-        return missing.isEmpty() && unknown.isEmpty();
-
-    }
-
-    private void handleFields(final ConstraintValidatorContext context, final String kind, final Set<String> fields) {
-        if (fields.isEmpty()) {
-            return;
-        }
-
-        context.disableDefaultConstraintViolation();
-
-        for (final String field : fields) {
-            context
-                    .buildConstraintViolationWithTemplate(kind + " field: " + field)
-                    .addConstraintViolation();
-        }
-    }
-
-    private Set<String> findUnknown(final AuthenticationServiceType type, final Map<String, Object> details) {
-        // we are using a TreeSet here to get a stable field order
-        final Set<String> fields = new TreeSet<>(details.keySet());
-        fields.removeAll(type.getDetailsFields().keySet());
-        return fields;
-    }
-
-    private Set<String> findMissing(final AuthenticationServiceType type, final Map<String, Object> details) {
-        // we are using a TreeSet here to get a stable field order
-        final Set<String> fields = new TreeSet<>(type.getMandatoryFields());
-        fields.removeAll(details.keySet());
-        return fields;
-    }
-}
-
diff --git a/console/console-init/oauth-proxy/bin/init.sh b/console/console-init/oauth-proxy/bin/init.sh
index 09e0170db..6998bbeb4 100755
--- a/console/console-init/oauth-proxy/bin/init.sh
+++ b/console/console-init/oauth-proxy/bin/init.sh
@@ -17,7 +17,7 @@ TARGET_DIR=${1-/apps}
 
 SSO_COOKIE_SECRET=$(python -c \
 'import os,base64; \
- print base64.urlsafe_b64encode(os.environ["SSO_COOKIE_SECRET"] if "SSO_COOKIE_SECRET" in os.environ else os.urandom(32))')
+ print base64.urlsafe_b64encode(os.environ["SSO_COOKIE_SECRET"] if "SSO_COOKIE_SECRET" in os.environ and os.environ["SSO_COOKIE_SECRET"] else os.urandom(32))')
 
 WELLKNOWN_DIR=${TARGET_DIR}/.well-known
 mkdir -p ${WELLKNOWN_DIR}
diff --git a/pkg/apis/admin/v1beta1/types.go b/pkg/apis/admin/v1beta1/types.go
index 9453b5431..4b52fd18f 100644
--- a/pkg/apis/admin/v1beta1/types.go
+++ b/pkg/apis/admin/v1beta1/types.go
@@ -96,6 +96,7 @@ type AuthenticationServiceSpecExternal struct {
 	Port             int                     `json:"port"`
 	CaCertSecret     *corev1.SecretReference `json:"caCertSecret,omitempty"`
 	ClientCertSecret *corev1.SecretReference `json:"clientCertSecret,omitempty"`
+	AllowOverride    bool                    `json:"allowOverride,omitempty"`
 }
 
 type AuthenticationServiceStatus struct {
@@ -122,7 +123,7 @@ type AuthenticationServiceList struct {
 type ConsoleService struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
-	Spec              ConsoleServiceSpec `json:"spec"`
+	Spec              ConsoleServiceSpec   `json:"spec"`
 	Status            ConsoleServiceStatus `json:"status"`
 }
 
@@ -137,9 +138,9 @@ type ConsoleServiceSpec struct {
 }
 
 type ConsoleServiceStatus struct {
-	Host             string                  `json:"host,omitempty"`
-	Port             int                     `json:"port,omitempty"`
-	CaCertSecret     *corev1.SecretReference `json:"caCertSecret,omitempty"`
+	Host         string                  `json:"host,omitempty"`
+	Port         int                     `json:"port,omitempty"`
+	CaCertSecret *corev1.SecretReference `json:"caCertSecret,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -150,5 +151,3 @@ type ConsoleServiceList struct {
 
 	Items []ConsoleService `json:"items"`
 }
-
-
diff --git a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java b/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
index 20a34017e..58d8fcd9f 100644
--- a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
+++ b/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
@@ -67,7 +67,6 @@ public abstract class OSBServiceBase {
     protected AddressSpace createAddressSpace(String instanceId, String name, String namespace, String type, String plan, String userId, String userName) throws Exception {
         AuthenticationService authService = new AuthenticationServiceBuilder()
                 .withType(AuthenticationServiceType.STANDARD)
-                .withDetails(Collections.emptyMap())
                 .build();
         AddressSpace addressSpace = new AddressSpaceBuilder()
                 .withNewMetadata()
diff --git a/templates/crds/authenticationservices.crd.yaml b/templates/crds/authenticationservices.crd.yaml
index 08809f169..bbad6b3a9 100644
--- a/templates/crds/authenticationservices.crd.yaml
+++ b/templates/crds/authenticationservices.crd.yaml
@@ -161,6 +161,8 @@ spec:
               - host
               - port
               properties:
+                allowOverride:
+                  type: boolean
                 host:
                   type: string
                 port:


Test patch:
diff --git a/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java b/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java
deleted file mode 100644
index 55ee58685..000000000
--- a/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.address.model;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-
-/**
- * Unit tests for {@link AuthenticationService}
- */
-public class AuthenticationServiceTest {
-
-    /**
-     * Test if two empty services are equal
-     */
-    @Test
-    public void testEqualsEmpty() {
-        assertEquality(true,
-                new AuthenticationServiceBuilder().build(),
-                new AuthenticationServiceBuilder().build());
-    }
-
-    /**
-     * Test if two services are equal having the same type
-     */
-    @Test
-    public void testEqualsSameType() {
-        assertEquality(true,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build());
-    }
-
-    /**
-     * Test if two services are not equal having the different types
-     */
-    @Test
-    public void testNonEqualsDifferentType() {
-        assertEquality(false,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.NONE)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build());
-    }
-
-    /**
-     * Test if two services are equal having the same type and the same details
-     */
-    @Test
-    public void testEqualsSameTypeSameDetails() {
-        final Map<String, Object> details1 = new HashMap<>();
-        final Map<String, Object> details2 = new HashMap<>();
-
-        for (final Map<String, Object> map : Arrays.asList(details1, details2)) {
-            map.put("string", "foo");
-            map.put("int", 1);
-            map.put("bool", true);
-        }
-
-        assertEquality(true,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .withDetails(details1)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .withDetails(details2)
-                        .build());
-    }
-
-    /**
-     * Test for equality
-     * 
-     * @param isEqual are values to be expected equal?
-     * @param v1 value 1
-     * @param v2 value 2
-     */
-    protected void assertEquality(final boolean isEqual, final Object v1, final Object v2) {
-        assertEquals(isEqual, v1.equals(v2));
-        assertEquals(isEqual, v2.equals(v1));
-        assertEquals(isEqual, v1.hashCode() == v2.hashCode());
-    }
-}
diff --git a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
index 5fcc93881..a76640372 100644
--- a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
+++ b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
@@ -192,12 +192,7 @@ public class SerializationTest {
                 .endEndpoint()
 
                 .withNewAuthenticationService()
-                    .withType(AuthenticationServiceType.EXTERNAL)
-                    .addToDetails("host", "my.example.com")
-                    .addToDetails("port", 5671)
-                    .addToDetails("caCertSecretName", "authservicesecret")
-                    .addToDetails("clientCertSecretName", "clientcertsecret")
-                    .addToDetails("saslInitHost", "my.example.com")
+                    .withName("myservice")
                 .endAuthenticationService()
 
                 .endSpec()
@@ -242,8 +237,7 @@ public class SerializationTest {
         assertThat(deserialized.getSpec().getEndpoints().get(0).getService(), is(addressSpace.getSpec().getEndpoints().get(0).getService()));
         assertThat(deserialized.getSpec().getEndpoints().get(0).getCert().getProvider(), is(addressSpace.getSpec().getEndpoints().get(0).getCert().getProvider()));
         assertThat(deserialized.getSpec().getEndpoints().get(0).getCert().getSecretName(), is(addressSpace.getSpec().getEndpoints().get(0).getCert().getSecretName()));
-        assertThat(deserialized.getSpec().getAuthenticationService().getType(), is(addressSpace.getSpec().getAuthenticationService().getType()));
-        assertThat(deserialized.getSpec().getAuthenticationService().getDetails(), is(addressSpace.getSpec().getAuthenticationService().getDetails()));
+        assertThat(deserialized.getSpec().getAuthenticationService().getName(), is(addressSpace.getSpec().getAuthenticationService().getName()));
         assertThat(addressSpace, is(deserialized));
     }
 
@@ -613,14 +607,20 @@ public class SerializationTest {
                 "  \"type\":\"standard\"," +
                 "  \"plan\":\"myplan\"," +
                 "  \"authenticationService\": {" +
-                "     \"type\": \"external\"" +
+                "     \"name\": \"external\"," +
+                "     \"host\": \"override.example.com\"," +
+                "     \"port\": 1234," +
+                "     \"realm\": \"override\"" +
                 "  }" +
                 "}" +
                 "}";
 
         ObjectMapper mapper = new ObjectMapper();
-        ConstraintViolationException exception = assertThrows(ConstraintViolationException.class, () -> validate(mapper.readValue(json, AddressSpace.class)));
-        assertEquals(3, exception.getConstraintViolations().size());
+        AddressSpace addressSpace = mapper.readValue(json, AddressSpace.class);
+        assertThat(addressSpace.getSpec().getAuthenticationService().getName(), is("external"));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getHost(), is("override.example.com"));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getPort(), is(1234));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getRealm(), is("override"));
     }
 
     @Test
@@ -635,12 +635,7 @@ public class SerializationTest {
                 "  \"type\":\"standard\"," +
                 "  \"plan\":\"myplan\"," +
                 "  \"authenticationService\": {" +
-                "     \"type\": \"external\"," +
-                "     \"details\": {" +
-                "       \"host\": \"my.example.com\"," +
-                "       \"saslInitHost\": \"localhost\"," +
-                "       \"port\": 1234" +
-                "     }" +
+                "     \"name\": \"myservice\"" +
                 "  }" +
                 "}" +
                 "}";
@@ -648,34 +643,6 @@ public class SerializationTest {
         ObjectMapper mapper = new ObjectMapper();
         AddressSpace addressSpace= mapper.readValue(json, AddressSpace.class);
         validate(addressSpace);
-        assertEquals("my.example.com", addressSpace.getSpec().getAuthenticationService().getDetails().get("host"));
-        assertEquals("localhost", addressSpace.getSpec().getAuthenticationService().getDetails().get("saslInitHost"));
-        assertEquals(1234, addressSpace.getSpec().getAuthenticationService().getDetails().get("port"));
-    }
-
-    @Test
-    public void testDeserializeAddressSpaceWithExtraAuthServiceValues() throws IOException {
-        String json = "{" +
-                "\"apiVersion\":\"enmasse.i/v1beta1\"," +
-                "\"kind\":\"AddressSpace\"," +
-                "\"metadata\":{" +
-                "  \"name\":\"myspace\"" +
-                "}," +
-                "\"spec\": {" +
-                "  \"type\":\"standard\"," +
-                "  \"plan\":\"myplan\"," +
-                "  \"authenticationService\": {" +
-                "     \"type\": \"standard\"," +
-                "     \"details\": {" +
-                "       \"host\": \"my.example.com\"" +
-                "     }" +
-                "  }" +
-                "}" +
-                "}";
-
-        ObjectMapper mapper = new ObjectMapper();
-        ConstraintViolationException exception = assertThrows(ConstraintViolationException.class, () -> validate(mapper.readValue(json, AddressSpace.class)));
-        assertEquals(1, exception.getConstraintViolations().size());
     }
 
     @Test
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
index a8edeed25..56874e903 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
@@ -295,22 +295,28 @@ public class AddressSpaceUtils {
     public static void waitForAddressSpaceDeleted(Kubernetes kubernetes, AddressSpace addressSpace) throws Exception {
         log.info("Waiting for AddressSpace {} to be deleted", addressSpace.getMetadata().getName());
         TimeoutBudget budget = new TimeoutBudget(10, TimeUnit.MINUTES);
-        waitForItems(addressSpace, budget, () -> kubernetes.listPods(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listConfigMaps(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listServices(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listSecrets(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listDeployments(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listStatefulSets(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listServiceAccounts(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-        waitForItems(addressSpace, budget, () -> kubernetes.listPersistentVolumeClaims(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))).size());
-    }
-
-    private static void waitForItems(AddressSpace addressSpace, TimeoutBudget budget, Callable<Integer> callable) throws Exception {
-        while (budget.timeLeft() >= 0 && callable.call() > 0) {
+        waitForItems(addressSpace, budget, () -> kubernetes.listPods(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listConfigMaps(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listServices(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listSecrets(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listDeployments(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listStatefulSets(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listServiceAccounts(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+        waitForItems(addressSpace, budget, () -> kubernetes.listPersistentVolumeClaims(Collections.singletonMap("infraUuid", getAddressSpaceInfraUuid(addressSpace))));
+    }
+
+    private static <T> void waitForItems(AddressSpace addressSpace, TimeoutBudget budget, Callable<List<T>> callable) throws Exception {
+        List<T> resources = null;
+        while (budget.timeLeft() >= 0) {
+            resources = callable.call();
+            if (resources == null || resources.isEmpty()) {
+                break;
+            }
             Thread.sleep(1000);
         }
-        if (callable.call() > 0) {
-            throw new TimeoutException("Timed out waiting for namespace " + addressSpace.getMetadata().getName() + " to disappear");
+        resources = callable.call();
+        if (resources != null && resources.size() > 0) {
+            throw new TimeoutException("Timed out waiting for namespace " + addressSpace.getMetadata().getName() + " to disappear. Resources left: " + resources);
         }
     }
 
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/GlobalConsoleTest.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/web/GlobalConsoleTest.java
index 341a0bd6e..b3c17a792 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/GlobalConsoleTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/bases/web/GlobalConsoleTest.java
@@ -57,6 +57,7 @@ public abstract class GlobalConsoleTest extends TestBase implements ISeleniumPro
     }
 
     protected void doTestCreateAddressSpace(AddressSpace addressSpace) throws Exception {
+        addToAddressSpacess(addressSpace);
         globalConsolePage = new GlobalConsolePage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser);
         globalConsolePage.openGlobalConsolePage();
         globalConsolePage.createAddressSpace(addressSpace);
@@ -66,6 +67,7 @@ public abstract class GlobalConsoleTest extends TestBase implements ISeleniumPro
     }
 
     protected void doTestConnectToAddressSpaceConsole(AddressSpace addressSpace) throws Exception {
+        addToAddressSpacess(addressSpace);
         globalConsolePage = new GlobalConsolePage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser);
         globalConsolePage.openGlobalConsolePage();
         globalConsolePage.createAddressSpace(addressSpace);
@@ -84,6 +86,7 @@ public abstract class GlobalConsoleTest extends TestBase implements ISeleniumPro
                 standardAuth.getMetadata().getName(),
                 AddressSpaceType.BROKERED,
                 AddressSpacePlans.BROKERED);
+        addToAddressSpacess(addressSpace);
 
         globalConsolePage = new GlobalConsolePage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser);
         globalConsolePage.openGlobalConsolePage();
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/common/api/CustomResourceDefinitionAddressSpacesTest.java b/systemtests/src/test/java/io/enmasse/systemtest/common/api/CustomResourceDefinitionAddressSpacesTest.java
index 78ad977e7..180eb6bc3 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/common/api/CustomResourceDefinitionAddressSpacesTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/common/api/CustomResourceDefinitionAddressSpacesTest.java
@@ -50,6 +50,7 @@ class CustomResourceDefinitionAddressSpacesTest extends TestBase {
     void testAddressSpaceCreateViaCmdRemoveViaApi() throws Exception {
         AddressSpace brokered = AddressSpaceUtils.createAddressSpaceObject("crd-addressspaces-test-foo", AddressSpaceType.BROKERED, AuthenticationServiceType.STANDARD);
         JsonObject addressSpacePayloadJson = AddressSpaceUtils.addressSpaceToJson(brokered);
+        addToAddressSpacess(brokered);
         createCR(addressSpacePayloadJson.toString());
         waitForAddressSpaceReady(brokered);
 
@@ -65,6 +66,7 @@ class CustomResourceDefinitionAddressSpacesTest extends TestBase {
     void testReplacePatchAddressSpace() throws Exception {
         AddressSpace standard = AddressSpaceUtils.createAddressSpaceObject("crd-addressspaces-test-foobar", AddressSpaceType.STANDARD, AddressSpacePlans.STANDARD_SMALL, AuthenticationServiceType.STANDARD);
         standard = new DoneableAddressSpace(standard).editSpec().withPlan(AddressSpacePlans.STANDARD_SMALL).endSpec().done();
+        addToAddressSpacess(standard);
         createCR(AddressSpaceUtils.addressSpaceToJson(standard).toString());
         addToAddressSpacess(standard);
         waitForAddressSpaceReady(standard);
@@ -108,6 +110,7 @@ class CustomResourceDefinitionAddressSpacesTest extends TestBase {
         UserCredentials user = Credentials.userCredentials();
         try (AddressApiClient apiClient = new AddressApiClient(kubernetes, namespace)) {
             AddressSpace brokered = AddressSpaceUtils.createAddressSpaceObject("crd-addressspaces-test-baz", AddressSpaceType.BROKERED, AuthenticationServiceType.STANDARD);
+            addToAddressSpacess(brokered);
             JsonObject addressSpacePayloadJson = AddressSpaceUtils.addressSpaceToJson(brokered);
 
             KubeCMDClient.loginUser(user.getUsername(), user.getPassword());
@@ -139,6 +142,8 @@ class CustomResourceDefinitionAddressSpacesTest extends TestBase {
             //===========================
             AddressSpace brokered = AddressSpaceUtils.createAddressSpaceObject("crd-brokered", AddressSpaceType.BROKERED, AuthenticationServiceType.STANDARD);
             AddressSpace standard = AddressSpaceUtils.createAddressSpaceObject("crd-standard", AddressSpaceType.STANDARD, AddressSpacePlans.STANDARD_SMALL, AuthenticationServiceType.STANDARD);
+            addToAddressSpacess(brokered);
+            addToAddressSpacess(standard);
 
             KubeCMDClient.loginUser(user.getUsername(), user.getPassword());
             KubeCMDClient.createNamespace(namespace);
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java b/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
index 73d9d4ff0..b8f0ca556 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
@@ -13,12 +13,8 @@ import io.enmasse.systemtest.amqp.UnauthorizedAccessException;
 import io.enmasse.systemtest.bases.TestBase;
 import io.enmasse.systemtest.cmdclients.KubeCMDClient;
 import io.enmasse.systemtest.executor.ExecutionResultData;
-import io.enmasse.systemtest.selenium.SeleniumManagement;
-import io.enmasse.systemtest.selenium.SeleniumProvider;
-import io.enmasse.systemtest.selenium.page.ConsoleWebPage;
 import io.enmasse.systemtest.utils.AddressSpaceUtils;
 import io.enmasse.systemtest.utils.AddressUtils;
-import io.enmasse.systemtest.utils.TestUtils;
 import io.enmasse.systemtest.utils.UserUtils;
 import io.enmasse.user.model.v1.DoneableUser;
 import io.enmasse.user.model.v1.Operation;
@@ -30,6 +26,8 @@ import org.slf4j.Logger;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
@@ -45,6 +43,7 @@ class UserApiTest extends TestBase {
     private static Logger log = CustomLogger.getLogger();
     private AddressSpace brokered = AddressSpaceUtils.createAddressSpaceObject("user-api-address-space-brokered", AddressSpaceType.BROKERED, AuthenticationServiceType.STANDARD);
     private AddressSpace standard = AddressSpaceUtils.createAddressSpaceObject("user-api-address-space-standard", AddressSpaceType.STANDARD, AddressSpacePlans.STANDARD_SMALL, AuthenticationServiceType.STANDARD);
+    private Map<AddressSpace, User> users = new HashMap<>();
 
     @BeforeEach
     void deployAddressSpaces() throws Exception {
@@ -54,6 +53,18 @@ class UserApiTest extends TestBase {
         if (!AddressSpaceUtils.existAddressSpace(addressApiClient, standard.getMetadata().getName())) {
             createAddressSpace(standard);
         }
+        users.clear();
+    }
+
+    @AfterEach
+    void cleanUsers() {
+        users.forEach((addressSpace, user) -> {
+            try {
+                removeUser(addressSpace, user.getSpec().getUsername());
+            } catch (Exception e) {
+                log.info("Clean: User not exists {}", user.getSpec().getUsername());
+            }
+        });
     }
 
     @BeforeAll
@@ -77,6 +88,7 @@ class UserApiTest extends TestBase {
                         .withOperations(Operation.send, Operation.recv).build()));
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
+        users.put(brokered, testUser);
 
         //create user
         assertThat(KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString()).getRetCode(), is(true));
@@ -100,6 +112,7 @@ class UserApiTest extends TestBase {
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
         userDefinitionPayload.getJsonObject("spec").getJsonArray("authorization").getJsonObject(0).getJsonArray("operations").add("unknown");
+        users.put(brokered, testUser);
 
         //create user
         ExecutionResultData createUserResponse = KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString());
@@ -131,6 +144,7 @@ class UserApiTest extends TestBase {
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
         userDefinitionPayload.getJsonObject("spec").getJsonArray("authorization").getJsonObject(0).getJsonArray("operations").add("posilani");
+        users.put(brokered, testUser);
 
         Throwable exception = assertThrows(ExecutionException.class, () -> getUserApiClient().createUser(userDefinitionPayload, HTTP_INTERNAL_ERROR));
         assertTrue(exception.getMessage().contains("value not one of declared Enum instance names: [send, view, recv, manage]"));
@@ -154,6 +168,7 @@ class UserApiTest extends TestBase {
                         .withOperations(Operation.send).build()));
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
+        users.put(brokered, testUser);
 
         //create user
         assertThat(KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString()).getRetCode(), is(true));
@@ -193,6 +208,7 @@ class UserApiTest extends TestBase {
                         .withAddresses(queue.getSpec().getAddress())
                         .withOperations(Operation.send).build()));
 
+        users.put(brokered, testUser);
         createUser(standard, testUser);
 
         AmqpClient client = amqpClientFactory.createQueueClient(standard);
@@ -219,6 +235,7 @@ class UserApiTest extends TestBase {
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
 
+        users.put(brokered, testUser);
         createUser(brokered, testUser);
 
         testUser = new DoneableUser(testUser).editSpec().withUsername("").endSpec().done();
@@ -255,6 +272,8 @@ class UserApiTest extends TestBase {
                         .withAddresses("*")
                         .withOperations(Operation.send, Operation.recv).build()));
 
+        users.put(brokered, testUser);
+        users.put(brokered, testUser2);
         createUser(brokered, testUser);
         createUser(brokered, testUser2);
 
@@ -281,6 +300,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
+        users.put(brokered, testUser);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser), HTTP_CREATED);
 
         //first char of username must be a-z0-9 (other symbols respond with HTTP:400)
@@ -298,6 +318,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
+        users.put(brokered, testUser3);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser3), HTTP_CREATED);
 
         //underscore is also allowed in user name (response HTTP:201)
@@ -306,7 +327,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
-
+        users.put(brokered, testUser4);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser4), HTTP_CREATED);
 
         //last char must also be a-z (other symbols respond with HTTP:400)
@@ -324,7 +345,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
-
+        users.put(brokered, testUser6);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser6), HTTP_CREATED);
 
         //Foreign symbols may not be used in username (response HTTP:400)
@@ -342,21 +363,23 @@ class UserApiTest extends TestBase {
         Address queue = AddressUtils.createQueueAddressObject("test-queue", DestinationPlan.STANDARD_SMALL_QUEUE);
         setAddresses(standard, queue);
         UserCredentials serviceAccount = new UserCredentials("test-service-account", "");
-        createUserServiceAccount(standard, serviceAccount, environment.namespace());
-        UserCredentials messagingUser = new UserCredentials("@@serviceaccount@@",
-                kubernetes.getServiceaccountToken(serviceAccount.getUsername(), environment.namespace()));
-        log.info("username: {}, password: {}", messagingUser.getUsername(), messagingUser.getPassword());
-
-        assertCanConnect(standard, messagingUser, Collections.singletonList(queue));
-
-        //delete user
-        assertThat("User deleting failed using oc cmd",
-                KubeCMDClient.deleteUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(true));
-        assertThat("User is still present",
-                KubeCMDClient.getUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(false));
-
-        assertCannotConnect(standard, messagingUser, Collections.singletonList(queue));
-
-        kubernetes.deleteServiceAccount("test-service-account", environment.namespace());
+        try {
+            createUserServiceAccount(standard, serviceAccount, environment.namespace());
+            UserCredentials messagingUser = new UserCredentials("@@serviceaccount@@",
+                    kubernetes.getServiceaccountToken(serviceAccount.getUsername(), environment.namespace()));
+            log.info("username: {}, password: {}", messagingUser.getUsername(), messagingUser.getPassword());
+
+            assertCanConnect(standard, messagingUser, Collections.singletonList(queue));
+
+            //delete user
+            assertThat("User deleting failed using oc cmd",
+                    KubeCMDClient.deleteUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(true));
+            assertThat("User is still present",
+                    KubeCMDClient.getUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(false));
+
+            assertCannotConnect(standard, messagingUser, Collections.singletonList(queue));
+        } finally {
+            kubernetes.deleteServiceAccount("test-service-account", environment.namespace());
+        }
     }
 }