Merge branch 'develop' of https://github.com/mifosio/identity into develop
diff --git a/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java b/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
index 33b746b..295900e 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
@@ -168,6 +168,35 @@
void deleteApplicationPermission(@PathVariable("applicationidentifier") String applicationIdentifier,
@PathVariable("permissionidentifier") String permittableEndpointGroupIdentifier);
+ @RequestMapping(value = "/applications/{applicationidentifier}/callendpointset", method = RequestMethod.POST,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ void createApplicationCallEndpointSet(@PathVariable("applicationidentifier") String applicationIdentifier, CallEndpointSet callEndpointSet);
+
+ @RequestMapping(value = "/applications/{applicationidentifier}/callendpointset/{callendpointsetidentifier}", method = RequestMethod.PUT,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ void changeApplicationCallEndpointSet(@PathVariable("applicationidentifier") String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") String callEndpointSetIdentifier,
+ CallEndpointSet callEndpointSet);
+
+ @RequestMapping(value = "/applications/{applicationidentifier}/callendpointset", method = RequestMethod.GET,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ List<CallEndpointSet> getApplicationCallEndpointSets(@PathVariable("applicationidentifier") String applicationIdentifier);
+
+ @RequestMapping(value = "/applications/{applicationidentifier}/callendpointset/{callendpointsetidentifier}", method = RequestMethod.GET,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ CallEndpointSet getApplicationCallEndpointSet(@PathVariable("applicationidentifier") String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") String callEndpointSetIdentifier);
+
+ @RequestMapping(value = "/applications/{applicationidentifier}/callendpointset/{callendpointsetidentifier}", method = RequestMethod.DELETE,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ void deleteApplicationCallEndpointSet(@PathVariable("applicationidentifier") String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") String callEndpointSetIdentifier);
+
@RequestMapping(value = "/applications/{applicationidentifier}/permissions/{permissionidentifier}/users/{useridentifier}/enabled", method = RequestMethod.PUT,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
diff --git a/api/src/main/java/io/mifos/identity/api/v1/domain/Authentication.java b/api/src/main/java/io/mifos/identity/api/v1/domain/Authentication.java
index 5faa63a..a5a4b53 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/domain/Authentication.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/domain/Authentication.java
@@ -17,6 +17,8 @@
import org.hibernate.validator.constraints.NotBlank;
+import javax.annotation.Nullable;
+import javax.validation.constraints.Null;
import java.util.Objects;
/**
@@ -38,8 +40,9 @@
/**
* If password expiration is in the past, then the tokens provided only allow the user to change his/her password.
+ * If password expiration is null then password will never expire.
*/
- @NotBlank
+ @Nullable
private String passwordExpiration;
public Authentication()
diff --git a/api/src/main/java/io/mifos/identity/api/v1/domain/CallEndpointSet.java b/api/src/main/java/io/mifos/identity/api/v1/domain/CallEndpointSet.java
new file mode 100644
index 0000000..ce4e402
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/domain/CallEndpointSet.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.domain;
+
+import io.mifos.core.lang.validation.constraints.ValidIdentifier;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class CallEndpointSet {
+ @ValidIdentifier
+ private String identifier;
+
+ @NotNull
+ private List<String> permittableEndpointGroupIdentifiers;
+
+ public CallEndpointSet() {
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ public List<String> getPermittableEndpointGroupIdentifiers() {
+ return permittableEndpointGroupIdentifiers;
+ }
+
+ public void setPermittableEndpointGroupIdentifiers(List<String> permittableEndpointGroupIdentifiers) {
+ this.permittableEndpointGroupIdentifiers = permittableEndpointGroupIdentifiers;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CallEndpointSet that = (CallEndpointSet) o;
+ return Objects.equals(identifier, that.identifier) &&
+ Objects.equals(permittableEndpointGroupIdentifiers, that.permittableEndpointGroupIdentifiers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(identifier, permittableEndpointGroupIdentifiers);
+ }
+
+ @Override
+ public String toString() {
+ return "CallEndpointSet{" +
+ "identifier='" + identifier + '\'' +
+ ", permittableEndpointGroupIdentifiers=" + permittableEndpointGroupIdentifiers +
+ '}';
+ }
+}
diff --git a/api/src/main/java/io/mifos/identity/api/v1/domain/Role.java b/api/src/main/java/io/mifos/identity/api/v1/domain/Role.java
index 04c55ee..a8d20bb 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/domain/Role.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/domain/Role.java
@@ -16,6 +16,7 @@
package io.mifos.identity.api.v1.domain;
import io.mifos.core.lang.validation.constraints.ValidIdentifier;
+import io.mifos.identity.api.v1.validation.ChangeableRole;
import org.hibernate.validator.constraints.ScriptAssert;
import org.springframework.util.Assert;
@@ -29,9 +30,9 @@
* @author Myrle Krantz
*/
@SuppressWarnings("unused")
-@ScriptAssert(lang = "javascript", script = "_this.identifier !== \"deactivated\"")
public class Role {
@ValidIdentifier
+ @ChangeableRole
private String identifier;
@NotNull
diff --git a/api/src/main/java/io/mifos/identity/api/v1/domain/RoleIdentifier.java b/api/src/main/java/io/mifos/identity/api/v1/domain/RoleIdentifier.java
index 6ba990d..ff18c63 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/domain/RoleIdentifier.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/domain/RoleIdentifier.java
@@ -15,6 +15,7 @@
*/
package io.mifos.identity.api.v1.domain;
+import io.mifos.identity.api.v1.validation.NotRootRole;
import org.hibernate.validator.constraints.NotBlank;
import java.util.Objects;
@@ -25,6 +26,7 @@
@SuppressWarnings({"unused", "WeakerAccess"})
public class RoleIdentifier {
@NotBlank
+ @NotRootRole
private String identifier;
public RoleIdentifier() {
diff --git a/api/src/main/java/io/mifos/identity/api/v1/events/ApplicationCallEndpointSetEvent.java b/api/src/main/java/io/mifos/identity/api/v1/events/ApplicationCallEndpointSetEvent.java
new file mode 100644
index 0000000..7d872be
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/events/ApplicationCallEndpointSetEvent.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.events;
+
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public class ApplicationCallEndpointSetEvent {
+ private String applicationIdentifier;
+ private String callEndpointSetIdentifier;
+
+ public ApplicationCallEndpointSetEvent() {
+ }
+
+ public ApplicationCallEndpointSetEvent(String applicationIdentifier, String callEndpointSetIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+
+ public String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ public void setApplicationIdentifier(String applicationIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ }
+
+ public String getCallEndpointSetIdentifier() {
+ return callEndpointSetIdentifier;
+ }
+
+ public void setCallEndpointSetIdentifier(String callEndpointSetIdentifier) {
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ApplicationCallEndpointSetEvent that = (ApplicationCallEndpointSetEvent) o;
+ return Objects.equals(applicationIdentifier, that.applicationIdentifier) &&
+ Objects.equals(callEndpointSetIdentifier, that.callEndpointSetIdentifier);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(applicationIdentifier, callEndpointSetIdentifier);
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationCallEndpointSetEvent{" +
+ "applicationIdentifier='" + applicationIdentifier + '\'' +
+ ", callEndpointSetIdentifier='" + callEndpointSetIdentifier + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/io/mifos/identity/api/v1/events/EventConstants.java b/api/src/main/java/io/mifos/identity/api/v1/events/EventConstants.java
index c97ec76..5ebacb9 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/events/EventConstants.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/events/EventConstants.java
@@ -38,6 +38,9 @@
String OPERATION_PUT_APPLICATION_SIGNATURE = "put-application-signature";
String OPERATION_DELETE_APPLICATION = "delete-application";
+ String OPERATION_POST_APPLICATION_CALLENDPOINTSET = "post-application-callendpointset";
+ String OPERATION_PUT_APPLICATION_CALLENDPOINTSET = "put-application-callendpointset";
+ String OPERATION_DELETE_APPLICATION_CALLENDPOINTSET = "delete-application-callendpointset";
String OPERATION_POST_APPLICATION_PERMISSION = "post-application-permission";
String OPERATION_DELETE_APPLICATION_PERMISSION = "delete-application-permission";
String OPERATION_PUT_APPLICATION_PERMISSION_USER_ENABLED = "put-application-permission-user-enabled";
@@ -56,6 +59,9 @@
String SELECTOR_PUT_APPLICATION_SIGNATURE = OPERATION_HEADER + " = '" + OPERATION_PUT_APPLICATION_SIGNATURE + "'";
String SELECTOR_DELETE_APPLICATION = OPERATION_HEADER + " = '" + OPERATION_DELETE_APPLICATION + "'";
+ String SELECTOR_POST_APPLICATION_CALLENDPOINTSET = OPERATION_HEADER + " = '" + OPERATION_POST_APPLICATION_CALLENDPOINTSET + "'";
+ String SELECTOR_PUT_APPLICATION_CALLENDPOINTSET = OPERATION_HEADER + " = '" + OPERATION_PUT_APPLICATION_CALLENDPOINTSET + "'";
+ String SELECTOR_DELETE_APPLICATION_CALLENDPOINTSET = OPERATION_HEADER + " = '" + OPERATION_DELETE_APPLICATION_CALLENDPOINTSET + "'";
String SELECTOR_POST_APPLICATION_PERMISSION = OPERATION_HEADER + " = '" + OPERATION_POST_APPLICATION_PERMISSION + "'";
String SELECTOR_DELETE_APPLICATION_PERMISSION = OPERATION_HEADER + " = '" + OPERATION_DELETE_APPLICATION_PERMISSION + "'";
String SELECTOR_PUT_APPLICATION_PERMISSION_USER_ENABLED = OPERATION_HEADER + " = '" + OPERATION_PUT_APPLICATION_PERMISSION_USER_ENABLED + "'";
diff --git a/api/src/main/java/io/mifos/identity/api/v1/validation/ChangeableRole.java b/api/src/main/java/io/mifos/identity/api/v1/validation/ChangeableRole.java
new file mode 100644
index 0000000..f2e0344
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/validation/ChangeableRole.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+ validatedBy = {CheckRoleChangeable.class}
+)
+public @interface ChangeableRole {
+ String message() default "The role 'pharaoh' cannot be changed or deleted.";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/api/src/main/java/io/mifos/identity/api/v1/validation/CheckNotRootRole.java b/api/src/main/java/io/mifos/identity/api/v1/validation/CheckNotRootRole.java
new file mode 100644
index 0000000..5a2e1a9
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/validation/CheckNotRootRole.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.validation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * @author Myrle Krantz
+ */
+public class CheckNotRootRole implements ConstraintValidator<NotRootRole, String> {
+ @Override
+ public void initialize(final NotRootRole constraintAnnotation) {
+
+ }
+
+ @Override
+ public boolean isValid(final String value, final ConstraintValidatorContext context) {
+ return !value.equals("pharaoh");
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/io/mifos/identity/api/v1/validation/CheckRoleChangeable.java b/api/src/main/java/io/mifos/identity/api/v1/validation/CheckRoleChangeable.java
new file mode 100644
index 0000000..5f09e9e
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/validation/CheckRoleChangeable.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.validation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("WeakerAccess")
+public class CheckRoleChangeable implements ConstraintValidator<ChangeableRole, String> {
+ @Override
+ public void initialize(final ChangeableRole constraintAnnotation) {
+
+ }
+
+ @Override
+ public boolean isValid(final String value, final ConstraintValidatorContext context) {
+ return isChangeableRoleIdentifier(value);
+ }
+
+ public static boolean isChangeableRoleIdentifier(final String value) {
+ return !value.equals("pharaoh") && !value.equals("deactivated");
+ }
+}
diff --git a/api/src/main/java/io/mifos/identity/api/v1/validation/NotRootRole.java b/api/src/main/java/io/mifos/identity/api/v1/validation/NotRootRole.java
new file mode 100644
index 0000000..4bb3ec9
--- /dev/null
+++ b/api/src/main/java/io/mifos/identity/api/v1/validation/NotRootRole.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+ validatedBy = {CheckNotRootRole.class}
+)
+public @interface NotRootRole {
+ String message() default "The role 'pharaoh' cannot be assigned to a user.";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/api/src/test/java/io/mifos/identity/api/v1/domain/CallEndpointSetTest.java b/api/src/test/java/io/mifos/identity/api/v1/domain/CallEndpointSetTest.java
new file mode 100644
index 0000000..3bc4e1a
--- /dev/null
+++ b/api/src/test/java/io/mifos/identity/api/v1/domain/CallEndpointSetTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.api.v1.domain;
+
+import io.mifos.core.test.domain.ValidationTest;
+import io.mifos.core.test.domain.ValidationTestCase;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(Parameterized.class)
+public class CallEndpointSetTest extends ValidationTest<CallEndpointSet> {
+
+ public CallEndpointSetTest(final ValidationTestCase<CallEndpointSet> testCase) {
+ super(testCase);
+ }
+
+ @Parameterized.Parameters
+ public static Collection testCases() {
+ final Collection<ValidationTestCase> ret = new ArrayList<>();
+
+ ret.add(new ValidationTestCase<CallEndpointSet>("validCase")
+ .adjustment(x -> {})
+ .valid(true));
+ ret.add(new ValidationTestCase<CallEndpointSet>("invalid identifier")
+ .adjustment(x -> x.setIdentifier(null))
+ .valid(false));
+ ret.add(new ValidationTestCase<CallEndpointSet>("null list")
+ .adjustment(x -> x.setPermittableEndpointGroupIdentifiers(null))
+ .valid(false));
+
+ return ret;
+ }
+
+ @Override
+ protected CallEndpointSet createValidTestSubject() {
+ final CallEndpointSet ret = new CallEndpointSet();
+ ret.setIdentifier("blah");
+ ret.setPermittableEndpointGroupIdentifiers(Collections.emptyList());
+ return ret;
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/test/java/io/mifos/identity/v1/domain/RoleIdentifierTest.java b/api/src/test/java/io/mifos/identity/v1/domain/RoleIdentifierTest.java
index 3711c4c..f73fc24 100644
--- a/api/src/test/java/io/mifos/identity/v1/domain/RoleIdentifierTest.java
+++ b/api/src/test/java/io/mifos/identity/v1/domain/RoleIdentifierTest.java
@@ -41,6 +41,12 @@
ret.add(new ValidationTestCase<RoleIdentifier>("spaceyIdentifier")
.adjustment(x -> x.setIdentifier(" "))
.valid(false));
+ ret.add(new ValidationTestCase<RoleIdentifier>("deactivated")
+ .adjustment(x -> x.setIdentifier("deactivated"))
+ .valid(true));
+ ret.add(new ValidationTestCase<RoleIdentifier>("pharaoh")
+ .adjustment(x -> x.setIdentifier("pharaoh"))
+ .valid(false));
return ret;
}
@@ -54,7 +60,7 @@
private RoleIdentifier createValidTestSubject()
{
- return new RoleIdentifier("pharaoh");
+ return new RoleIdentifier("scribe");
}
@Test()
diff --git a/api/src/test/java/io/mifos/identity/v1/domain/RoleTest.java b/api/src/test/java/io/mifos/identity/v1/domain/RoleTest.java
new file mode 100644
index 0000000..517a5cb
--- /dev/null
+++ b/api/src/test/java/io/mifos/identity/v1/domain/RoleTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.v1.domain;
+
+import io.mifos.core.test.domain.ValidationTest;
+import io.mifos.core.test.domain.ValidationTestCase;
+import io.mifos.identity.api.v1.domain.Role;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(Parameterized.class)
+public class RoleTest extends ValidationTest<Role> {
+
+ public RoleTest(final ValidationTestCase<Role> testCase) {
+ super(testCase);
+ }
+
+ @Parameterized.Parameters
+ public static Collection testCases() {
+ final Collection<ValidationTestCase> ret = new ArrayList<>();
+
+ ret.add(new ValidationTestCase<Role>("validCase")
+ .adjustment(x -> {})
+ .valid(true));
+ ret.add(new ValidationTestCase<Role>("deactivated")
+ .adjustment(x -> x.setIdentifier("deactivated"))
+ .valid(false));
+ ret.add(new ValidationTestCase<Role>("pharaoh")
+ .adjustment(x -> x.setIdentifier("pharaoh"))
+ .valid(false));
+
+ return ret;
+ }
+
+ @Override
+ protected Role createValidTestSubject() {
+ final Role ret = new Role();
+ ret.setIdentifier("blah");
+ ret.setPermissions(Collections.emptyList());
+ return ret;
+ }
+}
diff --git a/component-test/src/main/java/AbstractComponentTest.java b/component-test/src/main/java/AbstractComponentTest.java
index 63ce463..7f18da3 100644
--- a/component-test/src/main/java/AbstractComponentTest.java
+++ b/component-test/src/main/java/AbstractComponentTest.java
@@ -16,6 +16,7 @@
import io.mifos.anubis.api.v1.domain.AllowedOperation;
import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
import io.mifos.core.api.config.EnableApiFactory;
+import io.mifos.core.api.context.AutoGuest;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.ApiFactory;
import io.mifos.core.api.util.UserContextHolder;
@@ -105,7 +106,7 @@
if (!alreadyInitialized) {
try (final AutoUserContext ignored
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
- identityManager.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ identityManager.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
alreadyInitialized = true;
}
@@ -123,30 +124,8 @@
}
AutoUserContext enableAndLoginAdmin() throws InterruptedException {
- final Authentication adminAuthenticationToChangePassword =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
- Assert.assertNotNull(adminAuthenticationToChangePassword);
-
- {
- final boolean found = eventRecorder
- .wait(EventConstants.OPERATION_AUTHENTICATE, ADMIN_IDENTIFIER);
- Assert.assertTrue(found);
- }
-
- //Change password, then re-authenticate
- try (final AutoUserContext ignore = new AutoUserContext(ADMIN_IDENTIFIER, adminAuthenticationToChangePassword.getAccessToken()))
- {
- getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(Helpers.encodePassword(ADMIN_PASSWORD)));
-
- {
- final boolean found = eventRecorder
- .wait(EventConstants.OPERATION_PUT_USER_PASSWORD, ADMIN_IDENTIFIER);
- Assert.assertTrue(found);
- }
- }
-
final Authentication adminAuthentication =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Assert.assertNotNull(adminAuthentication);
{
@@ -155,8 +134,6 @@
Assert.assertTrue(found);
}
- eventRecorder.clear();
-
return new AutoUserContext(ADMIN_IDENTIFIER, adminAuthentication.getAccessToken());
}
@@ -165,20 +142,20 @@
* to access any other endpoint.
*/
String createUserWithNonexpiredPassword(final String password, final String role) throws InterruptedException {
- final String username = Helpers.generateRandomIdentifier("Ahmes");
+ final String username = testEnvironment.generateUniqueIdentifer("Ahmes");
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
- getTestSubject().createUser(new UserWithPassword(username, role, Helpers.encodePassword(password)));
+ getTestSubject().createUser(new UserWithPassword(username, role, TestEnvironment.encodePassword(password)));
{
final boolean found = eventRecorder.wait(EventConstants.OPERATION_POST_USER, username);
Assert.assertTrue(found);
}
- final Authentication passwordOnlyAuthentication = getTestSubject().login(username, Helpers.encodePassword(password));
+ final Authentication passwordOnlyAuthentication = getTestSubject().login(username, TestEnvironment.encodePassword(password));
try (final AutoUserContext ignore2 = new AutoUserContext(username, passwordOnlyAuthentication.getAccessToken()))
{
- getTestSubject().changeUserPassword(username, new Password(Helpers.encodePassword(password)));
+ getTestSubject().changeUserPassword(username, new Password(TestEnvironment.encodePassword(password)));
final boolean found = eventRecorder.wait(EventConstants.OPERATION_PUT_USER_PASSWORD, username);
Assert.assertTrue(found);
}
@@ -187,7 +164,7 @@
}
String generateRoleIdentifier() {
- return Helpers.generateRandomIdentifier("scribe");
+ return testEnvironment.generateUniqueIdentifer("scribe");
}
Role buildRole(final String identifier, final Permission... permission) {
@@ -211,7 +188,7 @@
return permission;
}
- private Permission buildSelfPermission() {
+ Permission buildSelfPermission() {
final Permission permission = new Permission();
permission.setAllowedOperations(AllowedOperation.ALL);
permission.setPermittableEndpointGroupIdentifier(PermittableGroupIds.SELF_MANAGEMENT);
@@ -238,7 +215,10 @@
}
AutoUserContext loginUser(final String userId, final String password) {
- final Authentication authentication = getTestSubject().login(userId, Helpers.encodePassword(password));
+ final Authentication authentication;
+ try (AutoUserContext ignored = new AutoGuest()) {
+ authentication = getTestSubject().login(userId, TestEnvironment.encodePassword(password));
+ }
return new AutoUserContext(userId, authentication.getAccessToken());
}
}
diff --git a/component-test/src/main/java/Helpers.java b/component-test/src/main/java/Helpers.java
index 5b43621..6251a7c 100644
--- a/component-test/src/main/java/Helpers.java
+++ b/component-test/src/main/java/Helpers.java
@@ -29,12 +29,4 @@
final String identifier) {
return users.stream().map(getIdentifier).filter(i -> i.equals(identifier)).findAny().isPresent();
}
-
- static String generateRandomIdentifier(final String prefix) {
- return prefix + Math.abs(new Random().nextInt());
- }
-
- static String encodePassword(final String password) {
- return Base64Utils.encodeToString(password.getBytes());
- }
}
diff --git a/component-test/src/main/java/TestApplications.java b/component-test/src/main/java/TestApplications.java
index a46a999..4dbf6f8 100644
--- a/component-test/src/main/java/TestApplications.java
+++ b/component-test/src/main/java/TestApplications.java
@@ -20,11 +20,9 @@
import io.mifos.core.api.util.NotFoundException;
import io.mifos.core.lang.security.RsaKeyPairFactory;
import io.mifos.identity.api.v1.PermittableGroupIds;
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
import io.mifos.identity.api.v1.domain.Permission;
-import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
-import io.mifos.identity.api.v1.events.ApplicationPermissionUserEvent;
-import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
-import io.mifos.identity.api.v1.events.EventConstants;
+import io.mifos.identity.api.v1.events.*;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -204,6 +202,56 @@
}
@Test
+ public void manageApplicationEndpointSet() throws InterruptedException {
+ try (final AutoUserContext ignored
+ = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
+ final ApplicationSignatureEvent appPlusSig = setApplicationSignature();
+
+ final String endpointSetIdentifier = testEnvironment.generateUniqueIdentifer("epset");
+ final CallEndpointSet endpointSet = new CallEndpointSet();
+ endpointSet.setIdentifier(endpointSetIdentifier);
+ endpointSet.setPermittableEndpointGroupIdentifiers(Collections.emptyList());
+
+ getTestSubject().createApplicationCallEndpointSet(appPlusSig.getApplicationIdentifier(), endpointSet);
+
+ Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_CALLENDPOINTSET,
+ new ApplicationCallEndpointSetEvent(appPlusSig.getApplicationIdentifier(), endpointSetIdentifier)));
+
+ final List<CallEndpointSet> applicationEndpointSets = getTestSubject().getApplicationCallEndpointSets(appPlusSig.getApplicationIdentifier());
+ Assert.assertTrue(applicationEndpointSets.contains(endpointSet));
+
+ final CallEndpointSet storedEndpointSet = getTestSubject().getApplicationCallEndpointSet(
+ appPlusSig.getApplicationIdentifier(),
+ endpointSetIdentifier);
+ Assert.assertEquals(endpointSet, storedEndpointSet);
+
+ endpointSet.setPermittableEndpointGroupIdentifiers(Collections.singletonList(PermittableGroupIds.ROLE_MANAGEMENT));
+ getTestSubject().changeApplicationCallEndpointSet(
+ appPlusSig.getApplicationIdentifier(),
+ endpointSetIdentifier,
+ endpointSet);
+
+ Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_PUT_APPLICATION_CALLENDPOINTSET,
+ new ApplicationCallEndpointSetEvent(appPlusSig.getApplicationIdentifier(), endpointSetIdentifier)));
+
+ final CallEndpointSet storedEndpointSet2 = getTestSubject().getApplicationCallEndpointSet(
+ appPlusSig.getApplicationIdentifier(),
+ endpointSetIdentifier);
+ Assert.assertEquals(endpointSet, storedEndpointSet2);
+
+ final List<CallEndpointSet> applicationEndpointSets2 = getTestSubject().getApplicationCallEndpointSets(appPlusSig.getApplicationIdentifier());
+ Assert.assertTrue(applicationEndpointSets2.size() == 1);
+
+ getTestSubject().deleteApplicationCallEndpointSet(appPlusSig.getApplicationIdentifier(), endpointSetIdentifier);
+ Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_DELETE_APPLICATION_CALLENDPOINTSET,
+ new ApplicationCallEndpointSetEvent(appPlusSig.getApplicationIdentifier(), endpointSetIdentifier)));
+
+ final List<CallEndpointSet> applicationEndpointSets3 = getTestSubject().getApplicationCallEndpointSets(appPlusSig.getApplicationIdentifier());
+ Assert.assertTrue(applicationEndpointSets3.isEmpty());
+ }
+ }
+
+ @Test
public void applicationIssuedRefreshTokenHappyCase() throws InterruptedException {
final ApplicationSignatureEvent appPlusSig;
final Permission rolePermission = buildRolePermission();
diff --git a/component-test/src/main/java/TestAuthentication.java b/component-test/src/main/java/TestAuthentication.java
index 0759568..0b58edd 100644
--- a/component-test/src/main/java/TestAuthentication.java
+++ b/component-test/src/main/java/TestAuthentication.java
@@ -22,6 +22,7 @@
import io.mifos.core.api.util.NotFoundException;
import io.mifos.core.lang.AutoTenantContext;
import io.mifos.core.lang.security.RsaPublicKeyBuilder;
+import io.mifos.core.test.env.TestEnvironment;
import io.mifos.identity.api.v1.domain.*;
import org.junit.Assert;
import org.junit.Test;
@@ -61,7 +62,7 @@
@Test(expected = NotFoundException.class)
public void testAdminIncorrectLogin() throws InterruptedException {
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword("set"));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword("set"));
Assert.fail("login with wrong password should fail with not found exception.");
}
@@ -70,7 +71,7 @@
try (final AutoUserContext ignored = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
try (final AutoTenantContext ignored2 = new AutoTenantContext())
{
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
}
Assert.fail("login without tenant header set should fail with bad request.");
@@ -80,7 +81,7 @@
public void testPermissionsCorrectInAdminToken() throws InterruptedException {
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
final Authentication adminAuthentication =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Assert.assertNotNull(adminAuthentication);
final TokenContent tokenContent = SystemSecurityEnvironment.getTokenContent(adminAuthentication.getAccessToken(), getPublicKey());
diff --git a/component-test/src/main/java/TestKeyRotation.java b/component-test/src/main/java/TestKeyRotation.java
index 92719c4..22c1cbb 100644
--- a/component-test/src/main/java/TestKeyRotation.java
+++ b/component-test/src/main/java/TestKeyRotation.java
@@ -16,9 +16,11 @@
import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.core.api.context.AutoGuest;
import io.mifos.core.api.context.AutoSeshat;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.NotFoundException;
+import io.mifos.core.test.env.TestEnvironment;
import io.mifos.identity.api.v1.domain.Authentication;
import org.junit.Assert;
import org.junit.Test;
@@ -51,7 +53,7 @@
final String systemToken = tenantApplicationSecurityEnvironment.getSystemSecurityEnvironment().systemToken(APP_NAME);
- try (final AutoSeshat ignored1 = new AutoSeshat(systemToken)) {
+ try (final AutoUserContext ignored1 = new AutoSeshat(systemToken)) {
//Create a signature set then test that it is listed.
final String timestamp = getTestSubject().createSignatureSet().getTimestamp();
{
@@ -60,8 +62,10 @@
}
- final Authentication adminAuthenticationOnFirstKeyset =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ final Authentication adminAuthenticationOnFirstKeyset;
+ try (final AutoUserContext ignored2 = new AutoGuest()) {
+ adminAuthenticationOnFirstKeyset = getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
+ }
//TODO: Assert.assertTrue(canAccessResources(adminAuthenticationOnFirstKeyset));
@@ -82,8 +86,10 @@
Assert.assertTrue(signatureSets.contains(timestamp2));
}
- final Authentication adminAuthenticationOnSecondKeyset =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ //final Authentication adminAuthenticationOnSecondKeyset;
+ try (final AutoUserContext ignored2 = new AutoGuest()) {
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
+ }
//TODO: Assert.assertTrue(canAccessResources(adminAuthenticationOnFirstKeyset));
//TODO: Assert.assertTrue(canAccessResources(adminAuthenticationOnSecondKeyset));
@@ -119,7 +125,7 @@
}
}
- private boolean canAccessResources(Authentication adminAuthentication) {
+ private boolean canAccessResources(final Authentication adminAuthentication) {
try (final AutoUserContext ignored = new AutoUserContext(ADMIN_IDENTIFIER, adminAuthentication.getAccessToken())) {
getTestSubject().getUsers();
return true;
diff --git a/component-test/src/main/java/TestPasswords.java b/component-test/src/main/java/TestPasswords.java
index f4c0260..7e0e677 100644
--- a/component-test/src/main/java/TestPasswords.java
+++ b/component-test/src/main/java/TestPasswords.java
@@ -15,15 +15,19 @@
*/
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.NotFoundException;
-import io.mifos.core.test.domain.DateStampChecker;
-import io.mifos.identity.api.v1.events.EventConstants;
+import io.mifos.core.test.domain.TimeStampChecker;
+import io.mifos.core.test.env.TestEnvironment;
import io.mifos.identity.api.v1.domain.Authentication;
import io.mifos.identity.api.v1.domain.Password;
import io.mifos.identity.api.v1.domain.UserWithPassword;
+import io.mifos.identity.api.v1.events.EventConstants;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Assert;
import org.junit.Test;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
/**
* @author Myrle Krantz
*/
@@ -34,7 +38,7 @@
final String username = createUserWithNonexpiredPassword(AHMES_PASSWORD, ADMIN_ROLE);
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
- final String newPassword = Helpers.encodePassword(
+ final String newPassword = TestEnvironment.encodePassword(
AHMES_PASSWORD + "make_it_a_little_longer");
{
@@ -48,7 +52,7 @@
try (final AutoUserContext ignore2 = new AutoUserContext(username, newPasswordAuthentication.getAccessToken()))
{
getTestSubject().createUser(new UserWithPassword("Ahmes_friend", "scribe",
- Helpers.encodePassword(AHMES_FRIENDS_PASSWORD)));
+ TestEnvironment.encodePassword(AHMES_FRIENDS_PASSWORD)));
Assert.fail("createUser should've thrown an exception because the password is admin reset.");
}
catch (final NotFoundException ex)
@@ -68,7 +72,7 @@
{
//Now it should be possible to create a user since the user changed the password herself.
getTestSubject().createUser(new UserWithPassword("Ahmes_friend", "scribe",
- Helpers.encodePassword(AHMES_FRIENDS_PASSWORD)));
+ TestEnvironment.encodePassword(AHMES_FRIENDS_PASSWORD)));
}
}
}
@@ -76,7 +80,7 @@
@Test
public void testAdminChangeAdminPassword() throws InterruptedException {
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
- final String newPassword = Helpers.encodePassword(
+ final String newPassword = TestEnvironment.encodePassword(
ADMIN_PASSWORD + "make_it_a_little_longer");
{
@@ -87,7 +91,7 @@
}
try {
- final String oldPassword = Helpers.encodePassword(ADMIN_PASSWORD);
+ final String oldPassword = TestEnvironment.encodePassword(ADMIN_PASSWORD);
getTestSubject().login(ADMIN_IDENTIFIER, oldPassword);
Assert.fail("Login with the old password should not succeed.");
} catch (final NotFoundException ignored) {
@@ -97,7 +101,7 @@
{
//Change the password back so the tests after this don't fail.
- getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(Helpers.encodePassword(ADMIN_PASSWORD)));
+ getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(TestEnvironment.encodePassword(ADMIN_PASSWORD)));
boolean found = eventRecorder.wait(EventConstants.OPERATION_PUT_USER_PASSWORD, ADMIN_IDENTIFIER);
Assert.assertTrue(found);
}
@@ -108,14 +112,11 @@
public void testUserChangeOwnPasswordButNotAdminPassword() throws InterruptedException {
final String username = createUserWithNonexpiredPassword(AHMES_PASSWORD, "scribe");
- final Authentication userAuthentication =
- getTestSubject().login(username, Helpers.encodePassword(AHMES_PASSWORD));
-
- try (AutoUserContext ignored = new AutoUserContext(username, userAuthentication.getAccessToken()))
+ try (final AutoUserContext ignored = loginUser(username, AHMES_PASSWORD))
{
final String newPassword = "new password";
{
- getTestSubject().changeUserPassword(username, new Password(Helpers.encodePassword(newPassword)));
+ getTestSubject().changeUserPassword(username, new Password(TestEnvironment.encodePassword(newPassword)));
boolean found = eventRecorder.wait(EventConstants.OPERATION_PUT_USER_PASSWORD, username);
Assert.assertTrue(found);
@@ -123,14 +124,14 @@
Thread.sleep(100);
- final DateStampChecker passwordExpirationChecker = DateStampChecker.inTheFuture(93);
- final Authentication userAuthenticationAfterPasswordChange = getTestSubject().login(username, Helpers.encodePassword(newPassword));
+ final TimeStampChecker passwordExpirationChecker = TimeStampChecker.inTheFutureWithWiggleRoom(Duration.ofDays(93), Duration.ofHours(24));
+ final Authentication userAuthenticationAfterPasswordChange = getTestSubject().login(username, TestEnvironment.encodePassword(newPassword));
final String passwordExpiration = userAuthenticationAfterPasswordChange.getPasswordExpiration();
passwordExpirationChecker.assertCorrect(passwordExpiration);
//noinspection EmptyCatchBlock
try {
- getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(Helpers.encodePassword(newPassword)));
+ getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(TestEnvironment.encodePassword(newPassword)));
Assert.fail("trying to change the admins password should fail.");
}
catch (final NotFoundException ex) {
@@ -140,7 +141,7 @@
try {
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(newPassword));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(newPassword));
Assert.fail("logging into admin with the new password should likewise fail.");
}
catch (final NotFoundException ex) {
@@ -166,4 +167,34 @@
}
}
+
+ @Test
+ public void activatedAntonyPasswordDoesntExpire() throws InterruptedException {
+
+ try (final AutoUserContext ignored = enableAndLoginAdmin()) {
+ final Authentication adminAuthentication =
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
+ Assert.assertEquals(null, adminAuthentication.getPasswordExpiration());
+ }
+ }
+
+ @Test
+ public void onlyAntonyCanSetAntonyPassword() throws InterruptedException {
+ try (final AutoUserContext ignored = enableAndLoginAdmin()) {
+
+ final String roleIdentifier = createRole(buildUserPermission(), buildSelfPermission(), buildRolePermission());
+ final String username = createUserWithNonexpiredPassword(AHMES_PASSWORD, roleIdentifier);
+
+ TimeUnit.SECONDS.sleep(1);
+ try (final AutoUserContext ignored2 = loginUser(username, AHMES_PASSWORD)) {
+ getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(TestEnvironment.encodePassword(AHMES_FRIENDS_PASSWORD)));
+ Assert.fail("Should not be able to change antony's password from any account other than antony's.");
+ }
+ catch (final IllegalArgumentException expected) {
+ //noinspection EmptyCatchBlock
+ }
+ }
+
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
+ }
}
diff --git a/component-test/src/main/java/TestPermittableGroups.java b/component-test/src/main/java/TestPermittableGroups.java
index cf1d462..d078849 100644
--- a/component-test/src/main/java/TestPermittableGroups.java
+++ b/component-test/src/main/java/TestPermittableGroups.java
@@ -51,7 +51,7 @@
@Test(expected = IllegalArgumentException.class)
public void createWithIllegalMethodThrows() throws InterruptedException {
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
- final String identifier = Helpers.generateRandomIdentifier("group");
+ final String identifier = testEnvironment.generateUniqueIdentifer("group");
final PermittableEndpoint permittableEndpoint = buildPermittableEndpoint();
permittableEndpoint.setMethod("blah");
@@ -65,7 +65,7 @@
@Test
public void create() throws InterruptedException {
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
- final String identifier = Helpers.generateRandomIdentifier("group");
+ final String identifier = testEnvironment.generateUniqueIdentifer("group");
final PermittableEndpoint permittableEndpoint = buildPermittableEndpoint();
final PermittableGroup group = buildPermittableGroup(identifier, permittableEndpoint);
diff --git a/component-test/src/main/java/TestProvisioning.java b/component-test/src/main/java/TestProvisioning.java
index d81a210..bef58b4 100644
--- a/component-test/src/main/java/TestProvisioning.java
+++ b/component-test/src/main/java/TestProvisioning.java
@@ -102,7 +102,7 @@
final String invalidSeshatToken = "notBearer";
try (final AutoSeshat ignored2 = new AutoSeshat(invalidSeshatToken)){
- testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
Assert.fail("The key had the wrong format. This should've failed.");
}
catch (final InvalidTokenException ignored2)
@@ -112,7 +112,7 @@
final String wrongSystemToken = systemTokenFromWrongKey();
try (final AutoSeshat ignored2 = new AutoSeshat(wrongSystemToken)){
- testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
Assert.fail("The key was signed by the wrong source. This should've failed.");
}
catch (final Exception e)
@@ -122,7 +122,7 @@
try (final AutoUserContext ignored2 = tenantApplicationSecurityEnvironment.createAutoSeshatContext("goober")) {
- testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
Assert.fail("The key was intended for a different tenant. This should've failed.");
}
catch (final Exception e)
@@ -131,7 +131,7 @@
}
try (final AutoUserContext ignored2 = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
- firstTenantSignatureSet = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ firstTenantSignatureSet = testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
final Signature applicationSignature = tenantApplicationSecurityEnvironment.getAnubis().getApplicationSignature(firstTenantSignatureSet.getTimestamp());
firstTenantIdentityManagerSignature = tenantApplicationSecurityEnvironment.getAnubis().getSignatureSet(firstTenantSignatureSet.getTimestamp()).getIdentityManagerSignature();
@@ -153,7 +153,7 @@
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
try (final AutoUserContext ignored2
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
- secondTenantSignatureSet = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ secondTenantSignatureSet = testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
final Signature secondTenantIdentityManagerSignature = tenantApplicationSecurityEnvironment.getAnubis().getApplicationSignature(secondTenantSignatureSet.getTimestamp());
Assert.assertNotEquals(firstTenantIdentityManagerSignature, secondTenantIdentityManagerSignature);
}
diff --git a/component-test/src/main/java/TestRefreshToken.java b/component-test/src/main/java/TestRefreshToken.java
index 47a4040..f27eb95 100644
--- a/component-test/src/main/java/TestRefreshToken.java
+++ b/component-test/src/main/java/TestRefreshToken.java
@@ -17,6 +17,7 @@
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.InvalidTokenException;
import io.mifos.core.test.domain.TimeStampChecker;
+import io.mifos.core.test.env.TestEnvironment;
import io.mifos.core.test.fixture.TenantDataStoreTestContext;
import io.mifos.identity.api.v1.domain.Authentication;
import io.mifos.identity.api.v1.domain.Password;
@@ -51,7 +52,7 @@
@Test(expected = InvalidTokenException.class)
public void adminLoginRefreshTokenShouldTimeOut() throws InterruptedException {
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Thread.sleep(TimeUnit.SECONDS.toMillis(REFRESH_TOKEN_TIME_TO_LIVE + 1));
@@ -60,7 +61,7 @@
@Test
public void afterAccessTokenExpiresRefreshTokenShouldAcquireNewAccessToken() throws InterruptedException {
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Thread.sleep(TimeUnit.SECONDS.toMillis(ACCESS_TOKEN_TIME_TO_LIVE + 1));
@@ -68,19 +69,19 @@
getTestSubject().refresh();
try (final AutoUserContext ignored = new AutoUserContext(ADMIN_IDENTIFIER, refreshAccessTokenAuthentication.getAccessToken())) {
- getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(Helpers.encodePassword(ADMIN_PASSWORD)));
+ getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(TestEnvironment.encodePassword(ADMIN_PASSWORD)));
}
}
@Test(expected = InvalidTokenException.class)
public void refreshTokenShouldGrantAccessOnlyToOneTenant()
{
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
try (final AutoUserContext ignored2
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
- getTestSubject().initialize(Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
getTestSubject().refresh();
@@ -90,7 +91,7 @@
@Test
public void expirationDatesShouldBeCorrectIsoDateTimes() throws InterruptedException {
final Authentication authentication =
- getTestSubject().login(ADMIN_IDENTIFIER, Helpers.encodePassword(ADMIN_PASSWORD));
+ getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
final TimeStampChecker preRefreshAccessTokenTimeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(ACCESS_TOKEN_TIME_TO_LIVE));
final TimeStampChecker refreshTokenTimeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(REFRESH_TOKEN_TIME_TO_LIVE));
diff --git a/component-test/src/main/java/TestRoles.java b/component-test/src/main/java/TestRoles.java
index bc934f0..16db3b8 100644
--- a/component-test/src/main/java/TestRoles.java
+++ b/component-test/src/main/java/TestRoles.java
@@ -25,6 +25,8 @@
import java.util.Collections;
import java.util.List;
+import static io.mifos.identity.internal.util.IdentityConstants.SU_ROLE;
+
/**
* @author Myrle Krantz
*/
@@ -113,4 +115,41 @@
Assert.assertEquals(role, changedRole);
}
}
-}
+
+ @Test
+ public void testChangePharaohRoleFails() throws InterruptedException {
+ try (final AutoUserContext ignore = enableAndLoginAdmin()) {
+ final Role referenceRole = getTestSubject().getRole(SU_ROLE);
+ final Role roleChangeRequest = buildRole(SU_ROLE, buildSelfPermission());
+
+ try {
+ getTestSubject().changeRole(SU_ROLE, roleChangeRequest);
+ Assert.fail("Should not be able to change the pharaoh role.");
+ }
+ catch (final IllegalArgumentException expected) {
+ //noinspection EmptyCatchBlock
+ }
+
+ final Role unChangedRole = getTestSubject().getRole(SU_ROLE);
+ Assert.assertEquals(referenceRole, unChangedRole);
+ }
+ }
+
+ @Test
+ public void testDeletePharaohRoleFails() throws InterruptedException {
+
+ try (final AutoUserContext ignore = enableAndLoginAdmin()) {
+ final Role adminRole = getTestSubject().getRole(ADMIN_ROLE);
+ try {
+ getTestSubject().deleteRole(ADMIN_ROLE);
+ Assert.fail("It should not be possible to delete the admin role.");
+ }
+ catch (final IllegalArgumentException expected) {
+ //noinspection EmptyCatchBlock
+ }
+
+ final Role adminRoleStillThere = getTestSubject().getRole(ADMIN_ROLE);
+ Assert.assertEquals(adminRole, adminRoleStillThere);
+ }
+ }
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/TestUsers.java b/component-test/src/main/java/TestUsers.java
index 5d56549..90b7663 100644
--- a/component-test/src/main/java/TestUsers.java
+++ b/component-test/src/main/java/TestUsers.java
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import io.mifos.anubis.api.v1.domain.AllowedOperation;
import io.mifos.core.api.context.AutoUserContext;
-import io.mifos.identity.api.v1.events.EventConstants;
+import io.mifos.core.test.env.TestEnvironment;
import io.mifos.identity.api.v1.PermittableGroupIds;
import io.mifos.identity.api.v1.domain.*;
+import io.mifos.identity.api.v1.events.EventConstants;
import org.junit.Assert;
import org.junit.Test;
@@ -25,6 +27,9 @@
import java.util.List;
import java.util.Set;
+import static io.mifos.identity.internal.util.IdentityConstants.SU_NAME;
+import static io.mifos.identity.internal.util.IdentityConstants.SU_ROLE;
+
/**
* @author Myrle Krantz
*/
@@ -44,13 +49,13 @@
}
final Authentication userAuthentication =
- getTestSubject().login(username, Helpers.encodePassword(AHMES_PASSWORD));
+ getTestSubject().login(username, TestEnvironment.encodePassword(AHMES_PASSWORD));
Assert.assertNotNull(userAuthentication);
try (final AutoUserContext ignored = new AutoUserContext(username, userAuthentication.getAccessToken())) {
getTestSubject().createUser(new UserWithPassword("Ahmes_friend", "scribe",
- Helpers.encodePassword(AHMES_FRIENDS_PASSWORD)));
+ TestEnvironment.encodePassword(AHMES_FRIENDS_PASSWORD)));
final boolean found = eventRecorder.wait(EventConstants.OPERATION_POST_USER, "Ahmes_friend");
Assert.assertTrue(found);
@@ -68,7 +73,7 @@
final String userIdentifier = createUserWithNonexpiredPassword(AHMES_PASSWORD, ADMIN_ROLE);
final Authentication ahmesAuthentication =
- getTestSubject().login(userIdentifier, Helpers.encodePassword(AHMES_PASSWORD));
+ getTestSubject().login(userIdentifier, TestEnvironment.encodePassword(AHMES_PASSWORD));
try (final AutoUserContext ignored = new AutoUserContext(userIdentifier, ahmesAuthentication.getAccessToken())) {
List<User> users = getTestSubject().getUsers();
@@ -91,6 +96,27 @@
}
@Test
+ public void testChangeAntonyRoleFails() throws InterruptedException {
+ final String userIdentifier = createUserWithNonexpiredPassword(AHMES_PASSWORD, ADMIN_ROLE);
+
+ final Authentication ahmesAuthentication =
+ getTestSubject().login(userIdentifier, TestEnvironment.encodePassword(AHMES_PASSWORD));
+
+ try (final AutoUserContext ignored = new AutoUserContext(userIdentifier, ahmesAuthentication.getAccessToken())) {
+ try {
+ getTestSubject().changeUserRole(SU_NAME, new RoleIdentifier("scribe"));
+ Assert.fail("Should not be able to change the role set for antony.");
+ }
+ catch (final IllegalArgumentException expected) {
+ //noinspection EmptyCatchBlock
+ }
+
+ final User antony = getTestSubject().getUser(SU_NAME);
+ Assert.assertEquals(SU_ROLE, antony.getRole());
+ }
+ }
+
+ @Test
public void testAdminProvisioning() throws InterruptedException {
try (final AutoUserContext ignore = enableAndLoginAdmin()) {
final List<Role> roleIdentifiers = getTestSubject().getRoles();
diff --git a/component-test/src/main/java/listener/ApplicationEventListener.java b/component-test/src/main/java/listener/ApplicationEventListener.java
index 936c95d..d56975a 100644
--- a/component-test/src/main/java/listener/ApplicationEventListener.java
+++ b/component-test/src/main/java/listener/ApplicationEventListener.java
@@ -17,10 +17,7 @@
import io.mifos.core.lang.config.TenantHeaderFilter;
import io.mifos.core.test.listener.EventRecorder;
-import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
-import io.mifos.identity.api.v1.events.ApplicationPermissionUserEvent;
-import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
-import io.mifos.identity.api.v1.events.EventConstants;
+import io.mifos.identity.api.v1.events.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.handler.annotation.Header;
@@ -95,4 +92,37 @@
final String payload) throws Exception {
eventRecorder.event(tenant, EventConstants.OPERATION_PUT_APPLICATION_PERMISSION_USER_ENABLED, payload, ApplicationPermissionUserEvent.class);
}
-}
+
+ @JmsListener(
+ subscription = EventConstants.DESTINATION,
+ destination = EventConstants.DESTINATION,
+ selector = EventConstants.SELECTOR_POST_APPLICATION_CALLENDPOINTSET
+ )
+ public void onCreateApplicationEndpointSet(
+ @Header(TenantHeaderFilter.TENANT_HEADER)final String tenant,
+ final String payload) throws Exception {
+ eventRecorder.event(tenant, EventConstants.OPERATION_POST_APPLICATION_CALLENDPOINTSET, payload, ApplicationCallEndpointSetEvent.class);
+ }
+
+ @JmsListener(
+ subscription = EventConstants.DESTINATION,
+ destination = EventConstants.DESTINATION,
+ selector = EventConstants.SELECTOR_PUT_APPLICATION_CALLENDPOINTSET
+ )
+ public void onSetApplicationEndpointSet(
+ @Header(TenantHeaderFilter.TENANT_HEADER)final String tenant,
+ final String payload) throws Exception {
+ eventRecorder.event(tenant, EventConstants.OPERATION_PUT_APPLICATION_CALLENDPOINTSET, payload, ApplicationCallEndpointSetEvent.class);
+ }
+
+ @JmsListener(
+ subscription = EventConstants.DESTINATION,
+ destination = EventConstants.DESTINATION,
+ selector = EventConstants.SELECTOR_DELETE_APPLICATION_CALLENDPOINTSET
+ )
+ public void onDeleteApplicationEndpointSet(
+ @Header(TenantHeaderFilter.TENANT_HEADER)final String tenant,
+ final String payload) throws Exception {
+ eventRecorder.event(tenant, EventConstants.OPERATION_DELETE_APPLICATION_CALLENDPOINTSET, payload, ApplicationCallEndpointSetEvent.class);
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/identity/internal/command/ChangeApplicationCallEndpointSetCommand.java b/service/src/main/java/io/mifos/identity/internal/command/ChangeApplicationCallEndpointSetCommand.java
new file mode 100644
index 0000000..c605cc9
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/command/ChangeApplicationCallEndpointSetCommand.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.command;
+
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class ChangeApplicationCallEndpointSetCommand {
+ private String applicationIdentifier;
+ private String callEndpointSetIdentifier;
+ private CallEndpointSet callEndpointSet;
+
+ public ChangeApplicationCallEndpointSetCommand() {
+ }
+
+ public ChangeApplicationCallEndpointSetCommand(
+ String applicationIdentifier,
+ String callEndpointSetIdentifier,
+ CallEndpointSet callEndpointSet) {
+ this.applicationIdentifier = applicationIdentifier;
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ this.callEndpointSet = callEndpointSet;
+ }
+
+ public String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ public void setApplicationIdentifier(String applicationIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ }
+
+ public String getCallEndpointSetIdentifier() {
+ return callEndpointSetIdentifier;
+ }
+
+ public void setCallEndpointSetIdentifier(String callEndpointSetIdentifier) {
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+
+ public CallEndpointSet getCallEndpointSet() {
+ return callEndpointSet;
+ }
+
+ public void setCallEndpointSet(CallEndpointSet callEndpointSet) {
+ this.callEndpointSet = callEndpointSet;
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/identity/internal/command/CreateApplicationCallEndpointSetCommand.java b/service/src/main/java/io/mifos/identity/internal/command/CreateApplicationCallEndpointSetCommand.java
new file mode 100644
index 0000000..694d5fe
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/command/CreateApplicationCallEndpointSetCommand.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.command;
+
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class CreateApplicationCallEndpointSetCommand {
+ private String applicationIdentifier;
+ private CallEndpointSet callEndpointSet;
+
+ public CreateApplicationCallEndpointSetCommand() {
+ }
+
+ public CreateApplicationCallEndpointSetCommand(String applicationIdentifier, CallEndpointSet callEndpointSet) {
+ this.applicationIdentifier = applicationIdentifier;
+ this.callEndpointSet = callEndpointSet;
+ }
+
+ public String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ public void setApplicationIdentifier(String applicationIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ }
+
+ public CallEndpointSet getCallEndpointSet() {
+ return callEndpointSet;
+ }
+
+ public void setEndpointSet(CallEndpointSet callEndpointSet) {
+ this.callEndpointSet = callEndpointSet;
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/identity/internal/command/DeleteApplicationCallEndpointSetCommand.java b/service/src/main/java/io/mifos/identity/internal/command/DeleteApplicationCallEndpointSetCommand.java
new file mode 100644
index 0000000..bb8025d
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/command/DeleteApplicationCallEndpointSetCommand.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.command;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class DeleteApplicationCallEndpointSetCommand {
+ private String applicationIdentifier;
+ private String callEndpointSetIdentifier;
+
+ public DeleteApplicationCallEndpointSetCommand() {
+ }
+
+ public DeleteApplicationCallEndpointSetCommand(String applicationIdentifier, String callEndpointSetIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+
+ public String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ public void setApplicationIdentifier(String applicationIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ }
+
+ public String getCallEndpointSetIdentifier() {
+ return callEndpointSetIdentifier;
+ }
+
+ public void setCallEndpointSetIdentifier(String callEndpointSetIdentifier) {
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+}
diff --git a/service/src/main/java/io/mifos/identity/internal/command/handler/ApplicationCommandHandler.java b/service/src/main/java/io/mifos/identity/internal/command/handler/ApplicationCommandHandler.java
index 9e4a62a..6b31a2f 100644
--- a/service/src/main/java/io/mifos/identity/internal/command/handler/ApplicationCommandHandler.java
+++ b/service/src/main/java/io/mifos/identity/internal/command/handler/ApplicationCommandHandler.java
@@ -18,11 +18,10 @@
import io.mifos.core.command.annotation.Aggregate;
import io.mifos.core.command.annotation.CommandHandler;
import io.mifos.core.command.annotation.EventEmitter;
-import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
-import io.mifos.identity.api.v1.events.ApplicationPermissionUserEvent;
-import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
-import io.mifos.identity.api.v1.events.EventConstants;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.identity.api.v1.events.*;
import io.mifos.identity.internal.command.*;
+import io.mifos.identity.internal.mapper.ApplicationCallEndpointSetMapper;
import io.mifos.identity.internal.mapper.PermissionMapper;
import io.mifos.identity.internal.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
@@ -31,20 +30,24 @@
/**
* @author Myrle Krantz
*/
+@SuppressWarnings("unused")
@Aggregate
@Component
public class ApplicationCommandHandler {
private final ApplicationSignatures applicationSignatures;
private final ApplicationPermissions applicationPermissions;
private final ApplicationPermissionUsers applicationPermissionUsers;
+ private final ApplicationCallEndpointSets applicationCallEndpointSets;
@Autowired
public ApplicationCommandHandler(final ApplicationSignatures applicationSignatures,
final ApplicationPermissions applicationPermissions,
- final ApplicationPermissionUsers applicationPermissionUsers) {
+ final ApplicationPermissionUsers applicationPermissionUsers,
+ final ApplicationCallEndpointSets applicationCallEndpointSets) {
this.applicationSignatures = applicationSignatures;
this.applicationPermissions = applicationPermissions;
this.applicationPermissionUsers = applicationPermissionUsers;
+ this.applicationCallEndpointSets = applicationCallEndpointSets;
}
@CommandHandler
@@ -90,4 +93,42 @@
applicationPermissionUsers.setEnabled(command.getApplicationIdentifier(), command.getPermittableGroupIdentifier(), command.getUserIdentifier(), command.isEnabled());
return new ApplicationPermissionUserEvent(command.getApplicationIdentifier(), command.getPermittableGroupIdentifier(), command.getUserIdentifier());
}
+
+ @CommandHandler
+ @EventEmitter(selectorName = EventConstants.OPERATION_HEADER, selectorValue = EventConstants.OPERATION_PUT_APPLICATION_CALLENDPOINTSET)
+ public ApplicationCallEndpointSetEvent process(final ChangeApplicationCallEndpointSetCommand command) {
+ applicationCallEndpointSets.get(command.getApplicationIdentifier(), command.getCallEndpointSetIdentifier())
+ .orElseThrow(() -> ServiceException.notFound("No application call endpoint '"
+ + command.getApplicationIdentifier() + "." + command.getCallEndpointSetIdentifier() + "'."));
+
+ final ApplicationCallEndpointSetEntity toSave = ApplicationCallEndpointSetMapper.mapToEntity(
+ command.getApplicationIdentifier(),
+ command.getCallEndpointSet());
+ applicationCallEndpointSets.change(toSave);
+ return new ApplicationCallEndpointSetEvent(command.getApplicationIdentifier(), command.getCallEndpointSetIdentifier());
+ }
+
+ @CommandHandler
+ @EventEmitter(selectorName = EventConstants.OPERATION_HEADER, selectorValue = EventConstants.OPERATION_POST_APPLICATION_CALLENDPOINTSET)
+ public ApplicationCallEndpointSetEvent process(final CreateApplicationCallEndpointSetCommand command) {
+ if (!applicationSignatures.signaturesExistForApplication(command.getApplicationIdentifier()))
+ throw ServiceException.notFound("No application '" + command.getApplicationIdentifier() + "'.");
+
+ final ApplicationCallEndpointSetEntity toSave = ApplicationCallEndpointSetMapper.mapToEntity(
+ command.getApplicationIdentifier(),
+ command.getCallEndpointSet());
+ applicationCallEndpointSets.add(toSave);
+ return new ApplicationCallEndpointSetEvent(command.getApplicationIdentifier(), command.getCallEndpointSet().getIdentifier());
+ }
+
+ @CommandHandler
+ @EventEmitter(selectorName = EventConstants.OPERATION_HEADER, selectorValue = EventConstants.OPERATION_DELETE_APPLICATION_CALLENDPOINTSET)
+ public ApplicationCallEndpointSetEvent process(final DeleteApplicationCallEndpointSetCommand command) {
+ applicationCallEndpointSets.get(command.getApplicationIdentifier(), command.getCallEndpointSetIdentifier())
+ .orElseThrow(() -> ServiceException.notFound("No application call endpoint '"
+ + command.getApplicationIdentifier() + "." + command.getCallEndpointSetIdentifier() + "'."));
+
+ applicationCallEndpointSets.delete(command.getApplicationIdentifier(), command.getCallEndpointSetIdentifier());
+ return new ApplicationCallEndpointSetEvent(command.getApplicationIdentifier(), command.getCallEndpointSetIdentifier());
+ }
}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java b/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
index 390a01c..e5bcdca 100644
--- a/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
+++ b/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
@@ -19,11 +19,10 @@
import io.mifos.anubis.api.v1.domain.AllowedOperation;
import io.mifos.anubis.api.v1.domain.TokenContent;
import io.mifos.anubis.api.v1.domain.TokenPermission;
+import io.mifos.anubis.provider.InvalidKeyTimestampException;
+import io.mifos.anubis.provider.TenantRsaKeyProvider;
import io.mifos.anubis.security.AmitAuthenticationException;
-import io.mifos.anubis.token.TenantAccessTokenSerializer;
-import io.mifos.anubis.token.TenantRefreshTokenSerializer;
-import io.mifos.anubis.token.TokenDeserializationResult;
-import io.mifos.anubis.token.TokenSerializationResult;
+import io.mifos.anubis.token.*;
import io.mifos.core.command.annotation.Aggregate;
import io.mifos.core.command.annotation.CommandHandler;
import io.mifos.core.lang.ApplicationName;
@@ -49,7 +48,10 @@
import org.springframework.util.Base64Utils;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -69,6 +71,7 @@
private final HashGenerator hashGenerator;
private final TenantAccessTokenSerializer tenantAccessTokenSerializer;
private final TenantRefreshTokenSerializer tenantRefreshTokenSerializer;
+ private final TenantRsaKeyProvider tenantRsaKeyProvider;
private final JmsTemplate jmsTemplate;
private final Gson gson;
private final Logger logger;
@@ -93,6 +96,8 @@
final TenantAccessTokenSerializer tenantAccessTokenSerializer,
@SuppressWarnings("SpringJavaAutowiringInspection")
final TenantRefreshTokenSerializer tenantRefreshTokenSerializer,
+ @SuppressWarnings("SpringJavaAutowiringInspection")
+ final TenantRsaKeyProvider tenantRsaKeyProvider,
final JmsTemplate jmsTemplate,
final ApplicationName applicationName,
@Qualifier(IdentityConstants.JSON_SERIALIZER_NAME) final Gson gson,
@@ -105,6 +110,7 @@
this.hashGenerator = hashGenerator;
this.tenantAccessTokenSerializer = tenantAccessTokenSerializer;
this.tenantRefreshTokenSerializer = tenantRefreshTokenSerializer;
+ this.tenantRsaKeyProvider = tenantRsaKeyProvider;
this.jmsTemplate = jmsTemplate;
this.gson = gson;
this.logger = logger;
@@ -141,7 +147,7 @@
throw AmitAuthenticationException.userPasswordCombinationNotFound();
}
- final LocalDate passwordExpiration = getExpiration(user);
+ final Optional<LocalDateTime> passwordExpiration = getExpiration(user);
final TokenSerializationResult accessToken = getAccessToken(
user.getIdentifier(),
@@ -155,7 +161,7 @@
return new AuthenticationCommandResponse(
accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
refreshToken.getToken(), DateConverter.toIsoString(refreshToken.getExpiration()),
- DateConverter.toIsoString(passwordExpiration));
+ passwordExpiration.map(DateConverter::toIsoString).orElse(null));
}
private PrivateSignatureEntity checkedGetPrivateSignature() {
@@ -176,19 +182,28 @@
return privateTenantInfo.get();
}
+ private class TenantIdentityRsaKeyProvider implements TenantApplicationRsaKeyProvider {
+ @Override
+ public PublicKey getApplicationPublicKey(final String tokenApplicationName, final String timestamp) throws InvalidKeyTimestampException {
+ if (!applicationName.toString().equals(tokenApplicationName))
+ throw new IllegalArgumentException("Currently only supporting refresh tokens issued by identity");
+ return tenantRsaKeyProvider.getPublicKey(timestamp);
+ }
+ }
+
@CommandHandler
public AuthenticationCommandResponse process(final RefreshTokenAuthenticationCommand command)
throws AmitAuthenticationException
{
final TokenDeserializationResult deserializedRefreshToken =
- tenantRefreshTokenSerializer.deserialize(command.getRefreshToken());
+ tenantRefreshTokenSerializer.deserialize(new TenantIdentityRsaKeyProvider(), command.getRefreshToken());
final PrivateTenantInfoEntity privateTenantInfo = checkedGetPrivateTenantInfo();
final PrivateSignatureEntity privateSignature = checkedGetPrivateSignature();
final UserEntity user = getUser(deserializedRefreshToken.getUserIdentifier());
- final LocalDate passwordExpiration = getExpiration(user);
+ final Optional<LocalDateTime> passwordExpiration = getExpiration(user);
final TokenSerializationResult accessToken = getAccessToken(
user.getIdentifier(),
@@ -198,12 +213,17 @@
return new AuthenticationCommandResponse(
accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
command.getRefreshToken(), DateConverter.toIsoString(deserializedRefreshToken.getExpiration()),
- DateConverter.toIsoString(passwordExpiration));
+ passwordExpiration.map(DateConverter::toIsoString).orElse(null));
}
- private LocalDate getExpiration(final UserEntity user)
+ private Optional<LocalDateTime> getExpiration(final UserEntity user)
{
- return LocalDate.ofEpochDay(user.getPasswordExpiresOn().getDaysSinceEpoch());
+ if (user.getIdentifier().equals(IdentityConstants.SU_NAME))
+ return Optional.empty();
+ else
+ return Optional.of(LocalDateTime.of(
+ LocalDate.ofEpochDay(user.getPasswordExpiresOn().getDaysSinceEpoch()), //Convert from cassandra LocalDate to java LocalDate.
+ LocalTime.MIDNIGHT));
}
private UserEntity getUser(final String identifier) throws AmitAuthenticationException {
@@ -257,7 +277,8 @@
private Set<TokenPermission> getTokenPermissions(
final UserEntity user,
- final LocalDate passwordExpiration,
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ final Optional<LocalDateTime> passwordExpiration,
final long gracePeriod) throws AmitAuthenticationException {
final Optional<RoleEntity> userRole = roles.get(user.getRole());
final Set<TokenPermission> tokenPermissions;
@@ -293,12 +314,15 @@
return tokenPermissions;
}
- static boolean pastExpiration(final LocalDate passwordExpiration) {
- return LocalDate.now().compareTo(passwordExpiration) >= 0;
+ static boolean pastExpiration(
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") final Optional<LocalDateTime> passwordExpiration) {
+ return passwordExpiration.map(x -> LocalDateTime.now().compareTo(x) >= 0).orElse(false);
}
- static boolean pastGracePeriod(final LocalDate passwordExpiration, final long gracePeriod) {
- return LocalDate.now().compareTo(passwordExpiration.plusDays(gracePeriod)) >= 0;
+ static boolean pastGracePeriod(
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") final Optional<LocalDateTime> passwordExpiration,
+ final long gracePeriod) {
+ return passwordExpiration.map(x -> (LocalDateTime.now().compareTo(x.plusDays(gracePeriod)) >= 0)).orElse(false);
}
private Stream<TokenPermission> mapPermissions(final PermissionType permission) {
diff --git a/service/src/main/java/io/mifos/identity/internal/command/handler/Provisioner.java b/service/src/main/java/io/mifos/identity/internal/command/handler/Provisioner.java
index 4dd0d10..66cf6fd 100644
--- a/service/src/main/java/io/mifos/identity/internal/command/handler/Provisioner.java
+++ b/service/src/main/java/io/mifos/identity/internal/command/handler/Provisioner.java
@@ -50,6 +50,7 @@
private final ApplicationSignatures applicationSignatures;
private final ApplicationPermissions applicationPermissions;
private final ApplicationPermissionUsers applicationPermissionUsers;
+ private final ApplicationCallEndpointSets applicationCallEndpointSets;
private final UserEntityCreator userEntityCreator;
private final Logger logger;
private final SaltGenerator saltGenerator;
@@ -74,6 +75,7 @@
final ApplicationSignatures applicationSignatures,
final ApplicationPermissions applicationPermissions,
final ApplicationPermissionUsers applicationPermissionUsers,
+ final ApplicationCallEndpointSets applicationCallEndpointSets,
final UserEntityCreator userEntityCreator,
@Qualifier(IdentityConstants.LOGGER_NAME) final Logger logger,
final SaltGenerator saltGenerator)
@@ -87,6 +89,7 @@
this.applicationSignatures = applicationSignatures;
this.applicationPermissions = applicationPermissions;
this.applicationPermissionUsers = applicationPermissionUsers;
+ this.applicationCallEndpointSets = applicationCallEndpointSets;
this.userEntityCreator = userEntityCreator;
this.logger = logger;
this.saltGenerator = saltGenerator;
@@ -107,6 +110,7 @@
applicationSignatures.buildTable();
applicationPermissions.buildTable();
applicationPermissionUsers.buildTable();
+ applicationCallEndpointSets.buildTable();
final SignatureEntity signatureEntity = signature.add(keys);
tenant.add(fixedSalt, passwordExpiresInDays, timeToChangePasswordAfterExpirationInDays);
diff --git a/service/src/main/java/io/mifos/identity/internal/mapper/ApplicationCallEndpointSetMapper.java b/service/src/main/java/io/mifos/identity/internal/mapper/ApplicationCallEndpointSetMapper.java
new file mode 100644
index 0000000..aba4685
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/mapper/ApplicationCallEndpointSetMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.mapper;
+
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
+import io.mifos.identity.internal.repository.ApplicationCallEndpointSetEntity;
+
+import java.util.Collections;
+
+/**
+ * @author Myrle Krantz
+ */
+public interface ApplicationCallEndpointSetMapper {
+ static ApplicationCallEndpointSetEntity mapToEntity(
+ final String applicationIdentifier,
+ final CallEndpointSet callEndpointSet)
+ {
+ final ApplicationCallEndpointSetEntity ret = new ApplicationCallEndpointSetEntity();
+ ret.setApplicationIdentifier(applicationIdentifier);
+ ret.setCallEndpointSetIdentifier(callEndpointSet.getIdentifier());
+ ret.setCallEndpointGroupIdentifiers(callEndpointSet.getPermittableEndpointGroupIdentifiers());
+ return ret;
+ }
+
+ static CallEndpointSet map(final ApplicationCallEndpointSetEntity entity) {
+ final CallEndpointSet ret = new CallEndpointSet();
+ ret.setIdentifier(entity.getCallEndpointSetIdentifier());
+ if (entity.getCallEndpointGroupIdentifiers() == null)
+ ret.setPermittableEndpointGroupIdentifiers(Collections.emptyList());
+ else
+ ret.setPermittableEndpointGroupIdentifiers(entity.getCallEndpointGroupIdentifiers());
+ return ret;
+ }
+}
diff --git a/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSetEntity.java b/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSetEntity.java
new file mode 100644
index 0000000..f692ae0
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSetEntity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.repository;
+
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+import java.util.List;
+
+/**
+ * @author Myrle Krantz
+ */
+@Table(name = ApplicationCallEndpointSets.TABLE_NAME)
+public class ApplicationCallEndpointSetEntity {
+ @PartitionKey
+ @Column(name = ApplicationCallEndpointSets.APPLICATION_IDENTIFIER_COLUMN)
+ private String applicationIdentifier;
+
+ @ClusteringColumn
+ @Column(name = ApplicationCallEndpointSets.CALLENDPOINTSET_IDENTIFIER_COLUMN)
+ private String callEndpointSetIdentifier;
+
+ @Column(name = ApplicationCallEndpointSets.CALLENDPOINT_GROUP_IDENTIFIERS_COLUMN)
+ private List<String> callEndpointGroupIdentifiers;
+
+ public ApplicationCallEndpointSetEntity() {
+ }
+
+ public String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ public void setApplicationIdentifier(String applicationIdentifier) {
+ this.applicationIdentifier = applicationIdentifier;
+ }
+
+ public String getCallEndpointSetIdentifier() {
+ return callEndpointSetIdentifier;
+ }
+
+ public void setCallEndpointSetIdentifier(String callEndpointSetIdentifier) {
+ this.callEndpointSetIdentifier = callEndpointSetIdentifier;
+ }
+
+ public List<String> getCallEndpointGroupIdentifiers() {
+ return callEndpointGroupIdentifiers;
+ }
+
+ public void setCallEndpointGroupIdentifiers(List<String> callEndpointGroupIdentifiers) {
+ this.callEndpointGroupIdentifiers = callEndpointGroupIdentifiers;
+ }
+}
diff --git a/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSets.java b/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSets.java
new file mode 100644
index 0000000..83bd201
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/internal/repository/ApplicationCallEndpointSets.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.internal.repository;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.Statement;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.core.schemabuilder.Create;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.datastax.driver.mapping.Mapper;
+import io.mifos.core.cassandra.core.CassandraSessionProvider;
+import io.mifos.core.cassandra.core.TenantAwareCassandraMapperProvider;
+import io.mifos.core.cassandra.core.TenantAwareEntityTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class ApplicationCallEndpointSets {
+ static final String TABLE_NAME = "isis_application_callendpointsets";
+ static final String APPLICATION_IDENTIFIER_COLUMN = "application_identifier";
+ static final String CALLENDPOINTSET_IDENTIFIER_COLUMN = "call_endpoint_set_identifier";
+ static final String CALLENDPOINT_GROUP_IDENTIFIERS_COLUMN = "call_endpoint_group_identifiers";
+
+ private final CassandraSessionProvider cassandraSessionProvider;
+ private final TenantAwareEntityTemplate tenantAwareEntityTemplate;
+ private final TenantAwareCassandraMapperProvider tenantAwareCassandraMapperProvider;
+
+ @Autowired
+ public ApplicationCallEndpointSets(
+ final CassandraSessionProvider cassandraSessionProvider,
+ final TenantAwareEntityTemplate tenantAwareEntityTemplate,
+ final TenantAwareCassandraMapperProvider tenantAwareCassandraMapperProvider) {
+ this.cassandraSessionProvider = cassandraSessionProvider;
+ this.tenantAwareEntityTemplate = tenantAwareEntityTemplate;
+ this.tenantAwareCassandraMapperProvider = tenantAwareCassandraMapperProvider;
+ }
+
+ public void buildTable() {
+
+ final Create createTableStatement =
+ SchemaBuilder.createTable(TABLE_NAME)
+ .addPartitionKey(APPLICATION_IDENTIFIER_COLUMN, DataType.text())
+ .addClusteringColumn(CALLENDPOINTSET_IDENTIFIER_COLUMN, DataType.text())
+ .addColumn(CALLENDPOINT_GROUP_IDENTIFIERS_COLUMN, DataType.list(DataType.text()));
+
+ cassandraSessionProvider.getTenantSession().execute(createTableStatement);
+ }
+
+ public void add(final ApplicationCallEndpointSetEntity entity) {
+ tenantAwareEntityTemplate.save(entity);
+ }
+
+ public void change(final ApplicationCallEndpointSetEntity instance) {
+ tenantAwareEntityTemplate.save(instance);
+ }
+
+ public Optional<ApplicationCallEndpointSetEntity> get(final String applicationIdentifier, final String callEndpointSetIdentifier)
+ {
+ final ApplicationCallEndpointSetEntity entity =
+ tenantAwareCassandraMapperProvider.getMapper(ApplicationCallEndpointSetEntity.class).get(applicationIdentifier, callEndpointSetIdentifier);
+
+ if (entity != null) {
+ Assert.notNull(entity.getApplicationIdentifier());
+ Assert.notNull(entity.getCallEndpointSetIdentifier());
+ }
+
+ return Optional.ofNullable(entity);
+ }
+
+ public List<ApplicationCallEndpointSetEntity> getAllForApplication(final String applicationIdentifier) {
+ final Mapper<ApplicationCallEndpointSetEntity> entityMapper = tenantAwareCassandraMapperProvider.getMapper(ApplicationCallEndpointSetEntity.class);
+ final Session tenantSession = cassandraSessionProvider.getTenantSession();
+
+ final Statement statement = QueryBuilder.select().from(TABLE_NAME).where(QueryBuilder.eq(APPLICATION_IDENTIFIER_COLUMN, applicationIdentifier));
+
+ return entityMapper.map(tenantSession.execute(statement)).all();
+ }
+
+ public void delete(final String applicationIdentifier, final String callEndpointSetIdentifier) {
+ final Optional<ApplicationCallEndpointSetEntity> toDelete = tenantAwareEntityTemplate.findById(ApplicationCallEndpointSetEntity.class, applicationIdentifier, callEndpointSetIdentifier);
+ toDelete.ifPresent(tenantAwareEntityTemplate::delete);
+ }
+}
diff --git a/service/src/main/java/io/mifos/identity/internal/service/ApplicationService.java b/service/src/main/java/io/mifos/identity/internal/service/ApplicationService.java
index 076baa5..9f001e7 100644
--- a/service/src/main/java/io/mifos/identity/internal/service/ApplicationService.java
+++ b/service/src/main/java/io/mifos/identity/internal/service/ApplicationService.java
@@ -16,13 +16,12 @@
package io.mifos.identity.internal.service;
import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
import io.mifos.identity.api.v1.domain.Permission;
+import io.mifos.identity.internal.mapper.ApplicationCallEndpointSetMapper;
import io.mifos.identity.internal.mapper.PermissionMapper;
import io.mifos.identity.internal.mapper.SignatureMapper;
-import io.mifos.identity.internal.repository.ApplicationPermissionUsers;
-import io.mifos.identity.internal.repository.ApplicationPermissions;
-import io.mifos.identity.internal.repository.ApplicationSignatureEntity;
-import io.mifos.identity.internal.repository.ApplicationSignatures;
+import io.mifos.identity.internal.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -40,14 +39,17 @@
private final ApplicationSignatures applicationSignaturesRepository;
private final ApplicationPermissions applicationPermissionsRepository;
private final ApplicationPermissionUsers applicationPermissionsUserRepository;
+ private final ApplicationCallEndpointSets applicationCallEndpointSets;
@Autowired
public ApplicationService(final ApplicationSignatures applicationSignaturesRepository,
final ApplicationPermissions applicationPermissionsRepository,
- final ApplicationPermissionUsers applicationPermissionsUserRepository) {
+ final ApplicationPermissionUsers applicationPermissionsUserRepository,
+ final ApplicationCallEndpointSets applicationCallEndpointSets) {
this.applicationSignaturesRepository = applicationSignaturesRepository;
this.applicationPermissionsRepository = applicationPermissionsRepository;
this.applicationPermissionsUserRepository = applicationPermissionsUserRepository;
+ this.applicationCallEndpointSets = applicationCallEndpointSets;
}
public List<String> getAllApplications() {
@@ -81,4 +83,21 @@
final String userIdentifier) {
return applicationPermissionsUserRepository.enabled(applicationIdentifier, permittableEndpointGroupIdentifier, userIdentifier);
}
+
+ public List<CallEndpointSet> getAllCallEndpointSetsForApplication(final String applicationIdentifier) {
+ return applicationCallEndpointSets.getAllForApplication(applicationIdentifier).stream()
+ .map(ApplicationCallEndpointSetMapper::map)
+ .collect(Collectors.toList());
+ }
+
+ public Optional<CallEndpointSet> getCallEndpointSetForApplication(
+ final String applicationIdentifier,
+ final String callEndpointSetIdentifier) {
+ return applicationCallEndpointSets.get(applicationIdentifier, callEndpointSetIdentifier)
+ .map(ApplicationCallEndpointSetMapper::map);
+ }
+
+ public boolean applicationCallEndpointSetExists(String applicationIdentifier, String callEndpointSetIdentifier) {
+ return applicationCallEndpointSets.get(applicationIdentifier, callEndpointSetIdentifier).isPresent();
+ }
}
diff --git a/service/src/main/java/io/mifos/identity/internal/service/TenantService.java b/service/src/main/java/io/mifos/identity/internal/service/TenantService.java
index b02279b..dee14ca 100644
--- a/service/src/main/java/io/mifos/identity/internal/service/TenantService.java
+++ b/service/src/main/java/io/mifos/identity/internal/service/TenantService.java
@@ -20,13 +20,17 @@
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.config.TenantSignatureRepository;
import io.mifos.core.lang.security.RsaKeyPairFactory;
+import io.mifos.core.lang.security.RsaPrivateKeyBuilder;
import io.mifos.identity.internal.mapper.SignatureMapper;
+import io.mifos.identity.internal.repository.PrivateSignatureEntity;
import io.mifos.identity.internal.repository.SignatureEntity;
import io.mifos.identity.internal.repository.Signatures;
import io.mifos.identity.internal.repository.Tenants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.security.PrivateKey;
+import java.security.interfaces.RSAPrivateKey;
import java.util.List;
import java.util.Optional;
@@ -100,6 +104,19 @@
return timestamp.flatMap(this::getApplicationSignature);
}
+ @Override
+ public Optional<RsaKeyPairFactory.KeyPairHolder> getLatestApplicationSigningKeyPair() {
+ final Optional<PrivateSignatureEntity> privateSignatureEntity = signatures.getPrivateSignature();
+ return privateSignatureEntity.map(x -> {
+ final String timestamp = x.getKeyTimestamp();
+ final PrivateKey privateKey = new RsaPrivateKeyBuilder()
+ .setPrivateKeyExp(x.getPrivateKeyExp())
+ .setPrivateKeyMod(x.getPrivateKeyMod())
+ .build();
+ return new RsaKeyPairFactory.KeyPairHolder(timestamp, null, (RSAPrivateKey)privateKey);
+ });
+ }
+
private Optional<String> getMostRecentTimestamp() {
return getAllSignatureSetKeyTimestamps().stream()
.max(String::compareTo);
diff --git a/service/src/main/java/io/mifos/identity/rest/ApplicationCallEndpointSetRestController.java b/service/src/main/java/io/mifos/identity/rest/ApplicationCallEndpointSetRestController.java
new file mode 100644
index 0000000..94812b1
--- /dev/null
+++ b/service/src/main/java/io/mifos/identity/rest/ApplicationCallEndpointSetRestController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.identity.rest;
+
+import io.mifos.anubis.annotation.AcceptedTokenType;
+import io.mifos.anubis.annotation.Permittable;
+import io.mifos.core.command.gateway.CommandGateway;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.identity.api.v1.domain.CallEndpointSet;
+import io.mifos.identity.internal.command.ChangeApplicationCallEndpointSetCommand;
+import io.mifos.identity.internal.command.CreateApplicationCallEndpointSetCommand;
+import io.mifos.identity.internal.command.DeleteApplicationCallEndpointSetCommand;
+import io.mifos.identity.internal.service.ApplicationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@RestController
+@RequestMapping("/applications/{applicationidentifier}/callendpointset")
+public class ApplicationCallEndpointSetRestController {
+ private final CommandGateway commandGateway;
+ private final ApplicationService applicationService;
+
+ @Autowired
+ public ApplicationCallEndpointSetRestController(
+ final CommandGateway commandGateway,
+ final ApplicationService applicationService) {
+ this.commandGateway = commandGateway;
+ this.applicationService = applicationService;
+ }
+
+ @RequestMapping(method = RequestMethod.POST,
+ produces = {MediaType.APPLICATION_JSON_VALUE},
+ consumes = {MediaType.ALL_VALUE})
+ @Permittable(value = AcceptedTokenType.SYSTEM)
+ public @ResponseBody
+ ResponseEntity<Void> createApplicationCallEndpointSet(
+ @PathVariable("applicationidentifier") final String applicationIdentifier,
+ @RequestBody final CallEndpointSet instance)
+ {
+ if (!applicationService.applicationExists(applicationIdentifier))
+ throw ServiceException.notFound("Application '"
+ + applicationIdentifier + "' doesn't exist.");
+
+ commandGateway.process(new CreateApplicationCallEndpointSetCommand(applicationIdentifier, instance));
+ return ResponseEntity.accepted().build();
+ }
+
+ @RequestMapping(value = "/{callendpointsetidentifier}", method = RequestMethod.PUT,
+ produces = {MediaType.APPLICATION_JSON_VALUE},
+ consumes = {MediaType.ALL_VALUE})
+ @Permittable(value = AcceptedTokenType.SYSTEM)
+ public @ResponseBody
+ ResponseEntity<Void> changeApplicationCallEndpointSet(
+ @PathVariable("applicationidentifier") String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") String callEndpointSetIdentifier,
+ @RequestBody final CallEndpointSet instance)
+ {
+ if (!applicationService.applicationCallEndpointSetExists(applicationIdentifier, callEndpointSetIdentifier))
+ throw ServiceException.notFound("Application call endpointset '"
+ + applicationIdentifier + "." + callEndpointSetIdentifier + "' doesn't exist.");
+
+ if (!callEndpointSetIdentifier.equals(instance.getIdentifier()))
+ throw ServiceException.badRequest("Instance identifiers may not be changed.");
+
+ commandGateway.process(new ChangeApplicationCallEndpointSetCommand(applicationIdentifier, callEndpointSetIdentifier, instance));
+ return ResponseEntity.accepted().build();
+ }
+
+ @RequestMapping(value = "/{callendpointsetidentifier}", method = RequestMethod.GET,
+ produces = {MediaType.APPLICATION_JSON_VALUE},
+ consumes = {MediaType.ALL_VALUE})
+ @Permittable(value = AcceptedTokenType.SYSTEM)
+ public @ResponseBody
+ ResponseEntity<CallEndpointSet> getApplicationCallEndpointSet(
+ @PathVariable("applicationidentifier") String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") String callEndpointSetIdentifier)
+ {
+ if (!applicationService.applicationCallEndpointSetExists(applicationIdentifier, callEndpointSetIdentifier))
+ throw ServiceException.notFound("Application call endpointset '"
+ + applicationIdentifier + "." + callEndpointSetIdentifier + "' doesn't exist.");
+
+ return applicationService.getCallEndpointSetForApplication(applicationIdentifier, callEndpointSetIdentifier)
+ .map(ResponseEntity::ok)
+ .orElseThrow(() -> ServiceException.notFound("Call endpointset for application " + applicationIdentifier + " and call endpointset identifier " + callEndpointSetIdentifier + "doesn't exist."));
+ }
+
+ @RequestMapping(method = RequestMethod.GET,
+ produces = {MediaType.APPLICATION_JSON_VALUE},
+ consumes = {MediaType.ALL_VALUE})
+ @Permittable(value = AcceptedTokenType.SYSTEM)
+ public @ResponseBody
+ ResponseEntity<List<CallEndpointSet>>
+ getApplicationCallEndpointSets(
+ @PathVariable("applicationidentifier") final String applicationIdentifier)
+ {
+ return ResponseEntity.ok(applicationService.getAllCallEndpointSetsForApplication(applicationIdentifier));
+ }
+
+ @RequestMapping(value = "/{callendpointsetidentifier}", method = RequestMethod.DELETE,
+ produces = {MediaType.APPLICATION_JSON_VALUE},
+ consumes = {MediaType.ALL_VALUE})
+ @Permittable(value = AcceptedTokenType.SYSTEM)
+ public @ResponseBody
+ ResponseEntity<Void> deleteApplicationCallEndpointSet(
+ @PathVariable("applicationidentifier") final String applicationIdentifier,
+ @PathVariable("callendpointsetidentifier") final String callEndpointSetIdentifier)
+ {
+ if (!applicationService.applicationCallEndpointSetExists(applicationIdentifier, callEndpointSetIdentifier))
+ throw ServiceException.notFound("Application call endpointset '"
+ + applicationIdentifier + "." + callEndpointSetIdentifier + "' doesn't exist.");
+ commandGateway.process(new DeleteApplicationCallEndpointSetCommand(applicationIdentifier, callEndpointSetIdentifier));
+ return ResponseEntity.accepted().build();
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/identity/rest/RoleRestController.java b/service/src/main/java/io/mifos/identity/rest/RoleRestController.java
index cf6b450..410a90b 100644
--- a/service/src/main/java/io/mifos/identity/rest/RoleRestController.java
+++ b/service/src/main/java/io/mifos/identity/rest/RoleRestController.java
@@ -21,10 +21,11 @@
import io.mifos.core.lang.ServiceException;
import io.mifos.identity.api.v1.PermittableGroupIds;
import io.mifos.identity.api.v1.domain.Role;
+import io.mifos.identity.api.v1.validation.CheckRoleChangeable;
+import io.mifos.identity.internal.command.ChangeRoleCommand;
import io.mifos.identity.internal.command.CreateRoleCommand;
import io.mifos.identity.internal.command.DeleteRoleCommand;
import io.mifos.identity.internal.service.RoleService;
-import io.mifos.identity.internal.command.ChangeRoleCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -94,6 +95,9 @@
@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.ROLE_MANAGEMENT)
public @ResponseBody ResponseEntity<Void> delete(@PathVariable(PathConstants.IDENTIFIER_PATH_VARIABLE) final String identifier)
{
+ if (!CheckRoleChangeable.isChangeableRoleIdentifier(identifier))
+ throw ServiceException.badRequest("Role with identifier: " + identifier + " cannot be deleted.");
+
checkIdentifier(identifier);
final DeleteRoleCommand deleteCommand = new DeleteRoleCommand(identifier);
@@ -108,6 +112,9 @@
public @ResponseBody ResponseEntity<Void> change(
@PathVariable(PathConstants.IDENTIFIER_PATH_VARIABLE) final String identifier, @RequestBody @Valid final Role instance)
{
+ if (!CheckRoleChangeable.isChangeableRoleIdentifier(identifier))
+ throw ServiceException.badRequest("Role with identifier: " + identifier + " cannot be changed.");
+
checkIdentifier(identifier);
if (!identifier.equals(instance.getIdentifier()))
diff --git a/service/src/main/java/io/mifos/identity/rest/UserRestController.java b/service/src/main/java/io/mifos/identity/rest/UserRestController.java
index 6e97b25..b20a38e 100644
--- a/service/src/main/java/io/mifos/identity/rest/UserRestController.java
+++ b/service/src/main/java/io/mifos/identity/rest/UserRestController.java
@@ -17,6 +17,7 @@
import io.mifos.anubis.annotation.AcceptedTokenType;
import io.mifos.anubis.annotation.Permittable;
+import io.mifos.core.api.util.UserContextHolder;
import io.mifos.core.command.gateway.CommandGateway;
import io.mifos.core.lang.ServiceException;
import io.mifos.identity.api.v1.PermittableGroupIds;
@@ -35,6 +36,7 @@
import java.util.List;
import java.util.Set;
+import static io.mifos.identity.internal.util.IdentityConstants.SU_NAME;
import static io.mifos.identity.rest.PathConstants.IDENTIFIER_PATH_VARIABLE;
@@ -98,6 +100,9 @@
@PathVariable(IDENTIFIER_PATH_VARIABLE) final String userIdentifier,
@RequestBody @Valid final RoleIdentifier roleIdentifier)
{
+ if (userIdentifier.equals(SU_NAME))
+ throw ServiceException.badRequest("Role of user with identifier: " + userIdentifier + " cannot be changed.");
+
checkIdentifier(userIdentifier);
final ChangeUserRoleCommand changeCommand = new ChangeUserRoleCommand(userIdentifier, roleIdentifier.getIdentifier());
@@ -127,6 +132,9 @@
@PathVariable(IDENTIFIER_PATH_VARIABLE) final String userIdentifier,
@RequestBody @Valid final Password password)
{
+ if (userIdentifier.equals(SU_NAME) && !UserContextHolder.checkedGetUser().equals(SU_NAME))
+ throw ServiceException.badRequest("Role of user with identifier: " + userIdentifier + " cannot be changed.");
+
checkIdentifier(userIdentifier);
checkPassword(password);