diff --git a/README.md b/README.md index ca8265a..3a8e024 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Some special features: - Implementations with and without restrictions - BasicScope (no restrictions) - MicroServiceScope - - microService.topic.mode.scope.extra + - microService.topic.mode.limitation.extra - Merging (and de-merging) - user.email.read+write+delete.all+beta -- Pre-defined scopes +- Pre-defined limitations - none (default) - own - self (only applicable to topics regarding a user itself) diff --git a/scope/src/main/java/com/gewia/common/scope/Scope.java b/scope/src/main/java/com/gewia/common/scope/Scope.java index 630a8e7..9a323e6 100644 --- a/scope/src/main/java/com/gewia/common/scope/Scope.java +++ b/scope/src/main/java/com/gewia/common/scope/Scope.java @@ -74,6 +74,18 @@ public Scope addScopePart(String scopePart) { */ public abstract Scope addScopePart(ScopePart scopePart); + /** + * Adds the given scopePart at the given index. + * + * @param scopePart the scope part to add + * @param index the index to add the scope part at + * + * @return this + * + * @since 1.0 + */ + public abstract Scope setScopePart(ScopePart scopePart, int index); + /** * Removes the scope part at the given index. * diff --git a/scope/src/main/java/com/gewia/common/scope/impl/BasicScope.java b/scope/src/main/java/com/gewia/common/scope/impl/BasicScope.java index 5ce02f5..b2cd53f 100644 --- a/scope/src/main/java/com/gewia/common/scope/impl/BasicScope.java +++ b/scope/src/main/java/com/gewia/common/scope/impl/BasicScope.java @@ -11,7 +11,7 @@ * * @since 1.0 */ -@RequiredArgsConstructor(access = AccessLevel.MODULE) +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class BasicScope extends Scope { /** @@ -52,5 +52,31 @@ public int getScopePartsSize() { return this.scopeParts.size(); } + @Override + public Scope setScopePart(ScopePart scopePart, int index) { + this.scopeParts.set(index, scopePart); + return this; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null) return false; + if (!(obj instanceof Scope)) return false; + + Scope scope = (Scope) obj; + List parts = scope.getScopeParts(); + for (int i = 0, partsSize = parts.size(); i < partsSize; i++) { + ScopePart scopePart = parts.get(i); + if (!scopePart.equals(this.getScopePart(i))) return false; + } + + return true; + } + + @Override + public int hashCode() { + return super.hashCode(); + } } diff --git a/scope/src/main/java/com/gewia/common/scope/impl/MicroServiceScope.java b/scope/src/main/java/com/gewia/common/scope/impl/MicroServiceScope.java new file mode 100644 index 0000000..736cfde --- /dev/null +++ b/scope/src/main/java/com/gewia/common/scope/impl/MicroServiceScope.java @@ -0,0 +1,106 @@ +package com.gewia.common.scope.impl; + +import com.gewia.common.scope.Scope; +import com.gewia.common.scope.ScopePart; +import com.gewia.common.scope.impl.util.Limitation; +import com.gewia.common.scope.impl.util.Mode; +import java.util.Collections; +import java.util.List; + +/** + * MicroServiceScope is specified by the following schema: 'microService.topic.mode.limitation.(extra)' . + * Example: user.user.write.all - lets the given application/user write/create all users. + */ +public class MicroServiceScope extends BasicScope { + + public MicroServiceScope(ScopePart microService, ScopePart topic, Mode mode, Limitation limitation, ScopePart... extra) { + super(); + + this.addScopePart(microService); + this.addScopePart(topic); + this.addScopePart(mode.getScopePart()); + this.addScopePart(limitation.getScopePart()); + + for (ScopePart scopePart : extra) this.addScopePart(scopePart); + } + + @Override + public Scope removeScopePart(int index) { + if (index <= 3) throw new UnsupportedOperationException("MicroServiceScopes have to fit the schema."); + return super.removeScopePart(index); + } + + /** + * Gets the currently available scope parts. + * + *

+ * The returned list is an unmodifiable list, because of the restrictions of {@link MicroServiceScope}. + *

+ * + * @return all scope parts + * + * @since 1.0 + */ + @Override + public List getScopeParts() { + return Collections.unmodifiableList(super.getScopeParts()); + } + + + /** + * Adds the given scopePart at the given index. + * + * @param scopePart the scope part to add + * @param index the index to add the scope part at + * + * @return this + * + * @throws IllegalArgumentException if the schema isn't fulfilled (e.g. invalid mode or limitation) + * + * @since 1.0 + */ + @Override + public Scope setScopePart(ScopePart scopePart, int index) { + if (index == 2 && !Mode.isMode(scopePart)) throw new IllegalArgumentException("Third index has to be a valid mode."); + if (index == 3 && !Limitation.isLimitation(scopePart)) throw new IllegalArgumentException("Fourth index has to be a valid limitation"); + + return this.setScopePart(scopePart, index); + } + + public ScopePart getMicroService() { + return this.getScopePart(0); + } + + public ScopePart getTopic() { + return this.getScopePart(1); + } + + public Mode getMode() { + return Mode.find(this.getScopePart(2).getContent()); + } + + public Limitation getLimitation() { + return Limitation.find(this.getScopePart(3).getContent()); + } + + public MicroServiceScope setMicroService(ScopePart microService) { + this.setScopePart(microService, 0); + return this; + } + + public MicroServiceScope setTopic(ScopePart topic) { + this.setScopePart(topic, 1); + return this; + } + + public MicroServiceScope setMode(Mode mode) { + this.setScopePart(mode.getScopePart(), 2); + return this; + } + + public MicroServiceScope setLimitation(Limitation limitation) { + this.setScopePart(limitation.getScopePart(), 3); + return this; + } + +} diff --git a/scope/src/main/java/com/gewia/common/scope/impl/ScopeFactory.java b/scope/src/main/java/com/gewia/common/scope/impl/ScopeFactory.java index 5c097ac..4c5b479 100644 --- a/scope/src/main/java/com/gewia/common/scope/impl/ScopeFactory.java +++ b/scope/src/main/java/com/gewia/common/scope/impl/ScopeFactory.java @@ -1,13 +1,16 @@ package com.gewia.common.scope.impl; import com.gewia.common.scope.Scope; +import com.gewia.common.scope.ScopePart; +import com.gewia.common.scope.impl.util.Limitation; +import com.gewia.common.scope.impl.util.Mode; public class ScopeFactory { /** - * Creates a {@link BasicScope} with not further restrictions. + * Creates a {@link BasicScope} with no further restrictions. * - * @return a {@link BasicScope} + * @return a new {@link BasicScope} * * @since 1.0 */ @@ -15,4 +18,40 @@ public static Scope createBasicScope() { return new BasicScope(); } + /** + * Creates a {@link MicroServiceScope} with it's specified restrictions. + * + * @param microService the micro service the scope belongs to + * @param topic the topic of the scope + * @param mode the mode of the scope + * @param limitation the limitation of the scope + * @param extra extra information + * + * @return a new {@link MicroServiceScope} + * + * @see MicroServiceScope + * @since 1.0 + */ + public static MicroServiceScope createMicroServiceScope(String microService, String topic, Mode mode, Limitation limitation, ScopePart... extra) { + return createMicroServiceScope(new ScopePart(microService), new ScopePart(topic), mode, limitation, extra); + } + + /** + * Creates a {@link MicroServiceScope} with it's specified restrictions. + * + * @param microService the micro service the scope belongs to + * @param topic the topic of the scope + * @param mode the mode of the scope + * @param limitation the limitation of the scope + * @param extra extra information + * + * @return a new {@link MicroServiceScope} + * + * @see MicroServiceScope + * @since 1.0 + */ + public static MicroServiceScope createMicroServiceScope(ScopePart microService, ScopePart topic, Mode mode, Limitation limitation, ScopePart... extra) { + return new MicroServiceScope(microService, topic, mode, limitation, extra); + } + } diff --git a/scope/src/main/java/com/gewia/common/scope/impl/util/Limitation.java b/scope/src/main/java/com/gewia/common/scope/impl/util/Limitation.java new file mode 100644 index 0000000..a8574cd --- /dev/null +++ b/scope/src/main/java/com/gewia/common/scope/impl/util/Limitation.java @@ -0,0 +1,29 @@ +package com.gewia.common.scope.impl.util; + +import com.gewia.common.scope.ScopePart; +import lombok.Getter; + +public enum Limitation { + + NONE, + OWN, + SELF, + ALL, + BETA; + + private static final Limitation[] VALUES = Limitation.values(); // #values() always creates a new copy of the array + + @Getter private final ScopePart scopePart = new ScopePart(this.name().toLowerCase()); + + public static Limitation find(String name) { + for (Limitation limitation : VALUES) + if (limitation.name().equalsIgnoreCase(name)) return limitation; + + return null; + } + + public static boolean isLimitation(ScopePart scopePart) { + return find(scopePart.getContent()) != null; + } + +} diff --git a/scope/src/main/java/com/gewia/common/scope/impl/util/Mode.java b/scope/src/main/java/com/gewia/common/scope/impl/util/Mode.java new file mode 100644 index 0000000..61f5f95 --- /dev/null +++ b/scope/src/main/java/com/gewia/common/scope/impl/util/Mode.java @@ -0,0 +1,27 @@ +package com.gewia.common.scope.impl.util; + +import com.gewia.common.scope.ScopePart; +import lombok.Getter; + +public enum Mode { + + READ, + WRITE, + DELETE; + + private static final Mode[] VALUES = Mode.values(); // #values() always creates a new copy of the array + + @Getter private final ScopePart scopePart = new ScopePart(this.name().toLowerCase()); + + public static Mode find(String name) { + for (Mode mode : VALUES) + if (mode.name().equalsIgnoreCase(name)) return mode; + + return null; + } + + public static boolean isMode(ScopePart scopePart) { + return find(scopePart.getContent()) != null; + } + +} diff --git a/scope/src/test/java/com/gewia/common/scope/test/BasicScopeTest.java b/scope/src/test/java/com/gewia/common/scope/test/BasicScopeTest.java index aa937fb..f1aeea4 100644 --- a/scope/src/test/java/com/gewia/common/scope/test/BasicScopeTest.java +++ b/scope/src/test/java/com/gewia/common/scope/test/BasicScopeTest.java @@ -15,10 +15,12 @@ public void basicCreateAndReadAndDeleteTest() { Assert.assertEquals("Scope parts size not zero", 0, scope.getScopePartsSize()); Assert.assertEquals("Output not empty", 0, scope.toString().length()); + scope.addScopeParts("user", "email", "read"); + Assert.assertEquals( - "Returned scope not the same", + "Scope does not fulfill expectation", scope, - scope.addScopeParts("user", "email", "read") + ScopeFactory.createBasicScope().addScopeParts("user", "email", "read") ); Assert.assertEquals("Scope output doesn't match", "user.email.read", scope.toString()); diff --git a/scope/src/test/java/com/gewia/common/scope/test/MicroServiceScopeTest.java b/scope/src/test/java/com/gewia/common/scope/test/MicroServiceScopeTest.java new file mode 100644 index 0000000..e4909d0 --- /dev/null +++ b/scope/src/test/java/com/gewia/common/scope/test/MicroServiceScopeTest.java @@ -0,0 +1,46 @@ +package com.gewia.common.scope.test; + +import com.gewia.common.scope.impl.ScopeFactory; +import com.gewia.common.scope.impl.MicroServiceScope; +import com.gewia.common.scope.impl.util.Limitation; +import com.gewia.common.scope.impl.util.Mode; +import org.junit.Assert; +import org.junit.Test; + +public class MicroServiceScopeTest { + + @Test + public void microServiceCreateAndReadAndDeleteTest() { + MicroServiceScope scope = ScopeFactory.createMicroServiceScope( + "testMicroService", + "test", + Mode.WRITE, + Limitation.ALL + ); + + Assert.assertFalse("Scope parts empty", scope.getScopeParts().isEmpty()); + Assert.assertEquals("Scope parts size not zero", 4, scope.getScopePartsSize()); + + Assert.assertEquals( + "Scope does not fulfill expectation", + scope, + ScopeFactory.createBasicScope().addScopeParts("testMicroService", "test", "write", "all") + ); + + Assert.assertEquals("Scope output doesn't match", "testMicroService.test.write.all", scope.toString()); + Assert.assertEquals("Scope parts do not match size (list access)", 4, scope.getScopeParts().size()); + Assert.assertEquals("Scope parts do not match size", 4, scope.getScopePartsSize()); + + Assert.assertThrows("Scope didn't catch out of bound remove", IndexOutOfBoundsException.class,() -> scope.removeScopePart(4)); + + for (int index = 0; index < 3; index++) { + int finalIndex = index; + Assert.assertThrows("Scope didn't catch not schema-compliant remove", UnsupportedOperationException.class,() -> scope.removeScopePart(finalIndex)); + } + + Assert.assertEquals("Scope output doesn't match", "testMicroService.test.write.all", scope.toString()); + Assert.assertEquals("Scope parts do not match size (list access)", 4, scope.getScopeParts().size()); + Assert.assertEquals("Scope parts do not match size", 4, scope.getScopePartsSize()); + } + +}