diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..16906fe
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,45 @@
+name: CI
+
+on: [ push, pull_request ]
+
+jobs:
+ test-and-coverage:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v3
+ with:
+ java-version: '8'
+ distribution: 'zulu'
+ cache: 'maven'
+ server-id: ossrh
+ server-username: OSSRH_JIRA_USERNAME
+ server-password: OSSRH_JIRA_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: GPG_PASSPHRASE
+
+ - name: Build with Maven
+ run: mvn clean test jacoco:report
+
+ - name: Upload To Codecov
+ uses: codecov/codecov-action@v1
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: 20
+
+ - name: Semantic Release
+ run: |
+ npm install -g @conveyal/maven-semantic-release semantic-release
+ semantic-release --prepare @conveyal/maven-semantic-release --publish @semantic-release/github,@conveyal/maven-semantic-release --verify-conditions @semantic-release/github,@conveyal/maven-semantic-release --verify-release @conveyal/maven-semantic-release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ OSSRH_JIRA_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }}
+ OSSRH_JIRA_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }}
diff --git a/README.md b/README.md
index 701ca79..6988994 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,76 @@
-# session-role-manager
\ No newline at end of file
+# session-role-manager
+[![codebeat badge](https://codebeat.co/badges/998c8e12-ffdd-4196-b2a2-8979d7f1ee8a)](https://codebeat.co/projects/github-com-jcasbin-session-role-manager-master)
+[![build](https://github.com/jcasbin/session-role-manager/actions/workflows/ci.yml/badge.svg)](https://github.com/jcasbin/session-role-manager/actions)
+[![codecov](https://codecov.io/github/jcasbin/session-role-manager/branch/master/graph/badge.svg?token=4YRFEQY7VK)](https://codecov.io/github/jcasbin/session-role-manager)
+[![javadoc](https://javadoc.io/badge2/org.casbin/session-role-manager/javadoc.svg)](https://javadoc.io/doc/org.casbin/session-role-manager)
+[![Maven Central](https://img.shields.io/maven-central/v/org.casbin/session-role-manager.svg)](https://mvnrepository.com/artifact/org.casbin/session-role-manager/latest)
+[![Discord](https://img.shields.io/discord/1022748306096537660?logo=discord&label=discord&color=5865F2)](https://discord.gg/S5UjpzGZjN)
+
+Session Role Manager is the [Session-based](https://en.wikipedia.org/wiki/Session_(computer_science)) role manager for [jCasbin](https://github.com/casbin/jcasbin). With this library, jCasbin can load session-based role hierarchy (user-role mapping) from jCasbin policy or save role hierarchy to it. The session is only active in the specified time range.
+
+## Installation
+```xml
+
+ org.casbin
+ session-role-manager
+ 1.0.0
+
+```
+
+## Example
+```java
+import org.casbin.jcasbin.main.Enforcer;
+import org.casbin.jcasbin.persist.file_adapter.FileAdapter;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class Example {
+ public static void main(String[] args) {
+ // Create a new Enforcer using the model path. The default role manager is used initially.
+ Enforcer e = new Enforcer("examples/rbac_model_with_sessions.conf");
+
+ // Manually set an adapter for the policy.
+ FileAdapter a = new FileAdapter("examples/rbac_policy_with_sessions.csv");
+ e.setAdapter(a);
+
+ // Use our custom role manager.
+ SessionRoleManager rm = new SessionRoleManager(10);
+ e.setRoleManager(rm);
+
+ // If our role manager relies on Casbin policy (e.g., reading "g" policy rules),
+ // we need to set the role manager before loading the policy.
+ e.loadPolicy();
+
+ // Current role inheritance tree (Time ranges shown in parentheses):
+ // delta echo foxtrott
+ // \ / \ /
+ // (0-20) \ (5-15) / \ (10-20) / (10-12)
+ // \ / \ /
+ // bravo charlie
+ // \ /
+ // (0-10) \ / (5-15)
+ // \ /
+ // alpha
+
+ // Test permissions for different time points
+ assertTrue(e.enforce("alpha", "data1", "read", "00"));
+ assertTrue(e.enforce("alpha", "data1", "read", "05"));
+ assertTrue(e.enforce("alpha", "data1", "read", "10"));
+ assertFalse(e.enforce("alpha", "data1", "read", "15"));
+ assertFalse(e.enforce("alpha", "data1", "read", "20"));
+
+ assertFalse(e.enforce("alpha", "data2", "read", "00"));
+ assertTrue(e.enforce("alpha", "data2", "read", "05"));
+ assertTrue(e.enforce("alpha", "data2", "read", "10"));
+ assertTrue(e.enforce("alpha", "data2", "read", "15"));
+ assertFalse(e.enforce("alpha", "data2", "read", "20"));
+
+ assertFalse(e.enforce("alpha", "data3", "read", "00"));
+ assertFalse(e.enforce("alpha", "data3", "read", "05"));
+ assertTrue(e.enforce("alpha", "data3", "read", "10"));
+ assertFalse(e.enforce("alpha", "data3", "read", "15"));
+ assertFalse(e.enforce("alpha", "data3", "read", "20"));
+ }
+}
+
+```
\ No newline at end of file
diff --git a/examples/rbac_model_with_sessions.conf b/examples/rbac_model_with_sessions.conf
new file mode 100644
index 0000000..be48baa
--- /dev/null
+++ b/examples/rbac_model_with_sessions.conf
@@ -0,0 +1,19 @@
+# Request definition
+[request_definition]
+r = sub, obj, act, time
+
+# Policy definition
+[policy_definition]
+p = sub, obj, act
+
+# Policy effect
+[policy_effect]
+e = some(where (p_eft == allow)) && !some(where (p_eft == deny))
+
+# Role definition
+[role_definition]
+g = _, _, _, _
+
+# Matchers
+[matchers]
+m = g(r.sub, p.sub, r.time) && r.obj == p.obj && r.act == p.act
diff --git a/examples/rbac_policy_with_sessions.csv b/examples/rbac_policy_with_sessions.csv
new file mode 100644
index 0000000..aedb791
--- /dev/null
+++ b/examples/rbac_policy_with_sessions.csv
@@ -0,0 +1,10 @@
+p, delta, data1, read
+p, echo, data2, read
+p, foxtrott, data3, read
+
+g, alpha, bravo, 00, 10
+g, alpha, charlie, 05, 15
+g, bravo, delta, 00, 20
+g, bravo, echo, 05, 15
+g, charlie, echo, 10, 20
+g, charlie, foxtrott, 10, 12
\ No newline at end of file
diff --git a/maven-settings.xml b/maven-settings.xml
new file mode 100644
index 0000000..9600d94
--- /dev/null
+++ b/maven-settings.xml
@@ -0,0 +1,22 @@
+
+
+
+ ossrh
+ ${OSSRH_JIRA_USERNAME}
+ ${OSSRH_JIRA_PASSWORD}
+
+
+
+
+ ossrh
+
+ true
+
+
+ gpg
+ ${GPG_KEY_NAME}
+ ${GPG_PASSPHRASE}
+
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1cd8523
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,170 @@
+
+
+ 4.0.0
+
+ org.casbin
+ session-role-manager
+ 1.0.0
+
+ Session based role manager for jCasbin
+ Load session-based role hierarchy from jCasbin policy or save role hierarchy to it
+ https://github.com/jcasbin/session-role-manager
+ 2024
+
+
+ Github
+ https://github.com/jcasbin/session-role-manager/issues
+
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+
+
+ The Apache Software License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://github.com/jcasbin/session-role-manager
+ git@github.com:jcasbin/session-role-manager.git
+ https://github.com/hsluoyz
+
+
+
+
+ Xu Tan
+ 1428427011@qq.com
+ https://github.com/tx2002
+
+
+
+
+ UTF-8
+
+
+
+
+ default
+
+ true
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.5.0
+ true
+
+ ossrh
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.1
+
+
+ package
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+
+ package
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.7.6.201602180812
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 8
+
+
+
+
+
+
+ ossrh
+ https://central.sonatype.com
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ org.casbin
+ jcasbin
+ 1.31.2
+
+
+ commons-collections
+ commons-collections
+ 3.2.2
+
+
+
+
diff --git a/src/main/java/org/casbin/rolemanager/Session.java b/src/main/java/org/casbin/rolemanager/Session.java
new file mode 100644
index 0000000..bba1101
--- /dev/null
+++ b/src/main/java/org/casbin/rolemanager/Session.java
@@ -0,0 +1,39 @@
+// Copyright 2024 The casbin Authors. All Rights Reserved.
+//
+// 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 org.casbin.rolemanager;
+
+public class Session {
+ private SessionRole role;
+ private String startTime;
+ private String endTime;
+
+ public Session(SessionRole role, String startTime, String endTime) {
+ this.role = role;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ public SessionRole getRole() {
+ return role;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ public String getEndTime() {
+ return endTime;
+ }
+}
diff --git a/src/main/java/org/casbin/rolemanager/SessionRole.java b/src/main/java/org/casbin/rolemanager/SessionRole.java
new file mode 100644
index 0000000..63f4fc0
--- /dev/null
+++ b/src/main/java/org/casbin/rolemanager/SessionRole.java
@@ -0,0 +1,92 @@
+// Copyright 2024 The casbin Authors. All Rights Reserved.
+//
+// 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 org.casbin.rolemanager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SessionRole {
+ private String name;
+ private List sessions;
+
+ public SessionRole(String name) {
+ this.name = name;
+ this.sessions = new ArrayList<>();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void addSession(Session session) {
+ this.sessions.add(session);
+ }
+
+ public void deleteSessions(String sessionName) {
+ sessions.removeIf(s -> s.getRole().getName().equals(sessionName));
+ }
+
+ public List getSessionRoles(String requestTime) {
+ List roles = new ArrayList<>();
+ for (Session session : sessions) {
+ if (session.getStartTime().compareTo(requestTime) <= 0 && session.getEndTime().compareTo(requestTime) >= 0) {
+ if (!roles.contains(session.getRole().getName())) {
+ roles.add(session.getRole().getName());
+ }
+ }
+ }
+ return roles;
+ }
+
+ public boolean hasValidSession(String roleName, int hierarchyLevel, String requestTime) {
+ if (hierarchyLevel == 1) {
+ return this.name.equals(roleName);
+ }
+
+ for (Session session : sessions) {
+ if (session.getStartTime().compareTo(requestTime) <= 0 && session.getEndTime().compareTo(requestTime) >= 0) {
+ if (session.getRole().getName().equals(roleName)) {
+ return true;
+ }
+ if (session.getRole().hasValidSession(roleName, hierarchyLevel - 1, requestTime)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasDirectRole(String roleName, String requestTime) {
+ for (Session session : sessions) {
+ if (session.getRole().getName().equals(roleName) &&
+ session.getStartTime().compareTo(requestTime) <= 0 &&
+ session.getEndTime().compareTo(requestTime) >= 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(name + " < ");
+ for (int i = 0; i < sessions.size(); i++) {
+ if (i > 0) sb.append(", ");
+ sb.append(sessions.get(i).getRole().getName())
+ .append(" (until: ").append(sessions.get(i).getEndTime()).append(")");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/casbin/rolemanager/SessionRoleManager.java b/src/main/java/org/casbin/rolemanager/SessionRoleManager.java
new file mode 100644
index 0000000..56a3095
--- /dev/null
+++ b/src/main/java/org/casbin/rolemanager/SessionRoleManager.java
@@ -0,0 +1,185 @@
+// Copyright 2024 The casbin Authors. All Rights Reserved.
+//
+// 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 org.casbin.rolemanager;
+
+import org.casbin.jcasbin.rbac.RoleManager;
+import java.util.*;
+
+/**
+ * SessionRoleManager is an implementation of the RoleManager interface that supports temporal role inheritance
+ * using sessions with a defined start and end time for role inheritance.
+ */
+public class SessionRoleManager implements RoleManager {
+ private Map allRoles; // Stores all roles with their respective session information
+ private int maxHierarchyLevel; // Maximum allowed hierarchy level for role inheritance
+
+ /**
+ * Constructor for creating an instance of SessionRoleManager.
+ *
+ * @param maxHierarchyLevel The maximum depth of role hierarchy.
+ */
+ public SessionRoleManager(int maxHierarchyLevel) {
+ this.allRoles = new HashMap<>();
+ this.maxHierarchyLevel = maxHierarchyLevel;
+ }
+
+ /**
+ * Checks if a role exists in the manager.
+ *
+ * @param name Name of the role.
+ * @return True if the role exists, false otherwise.
+ */
+ private boolean hasRole(String name) {
+ return allRoles.containsKey(name);
+ }
+
+ /**
+ * Creates a new role if it doesn't already exist.
+ *
+ * @param name Name of the role.
+ * @return The created or existing role.
+ */
+ private SessionRole createRole(String name) {
+ if (!hasRole(name)) {
+ allRoles.put(name, new SessionRole(name));
+ }
+ return allRoles.get(name);
+ }
+
+ /**
+ * Clears all stored roles and resets the manager.
+ */
+ @Override
+ public void clear() {
+ allRoles.clear();
+ }
+
+ /**
+ * Adds an inheritance link between two roles, valid for a specific time range.
+ *
+ * @param name1 Name of the first role (child role).
+ * @param name2 Name of the second role (parent role).
+ * @param timeRange The time range (start time, end time) for when the link is active.
+ * @throws IllegalArgumentException if the time range is not exactly 2 elements.
+ */
+ @Override
+ public void addLink(String name1, String name2, String... timeRange) {
+ if (timeRange.length != 2) {
+ throw new IllegalArgumentException("Time range must consist of start and end times.");
+ }
+ String startTime = timeRange[0];
+ String endTime = timeRange[1];
+
+ SessionRole role1 = createRole(name1);
+ SessionRole role2 = createRole(name2);
+
+ Session session = new Session(role2, startTime, endTime);
+ role1.addSession(session);
+ }
+
+ /**
+ * Deletes the inheritance link between two roles.
+ *
+ * @param name1 Name of the first role (child role).
+ * @param name2 Name of the second role (parent role).
+ */
+ @Override
+ public void deleteLink(String name1, String name2, String... unused) {
+ if (!hasRole(name1) || !hasRole(name2)) {
+ throw new IllegalArgumentException("Role not found: " + name1 + " or " + name2);
+ }
+
+ SessionRole role1 = createRole(name1);
+ role1.deleteSessions(name2);
+ }
+
+ /**
+ * Checks if a role inherits another role at a specific time.
+ *
+ * @param name1 Name of the first role (child role).
+ * @param name2 Name of the second role (parent role).
+ * @param requestTime The time to check the role inheritance.
+ * @return True if role1 inherits role2 at the given time, false otherwise.
+ */
+ @Override
+ public boolean hasLink(String name1, String name2, String... requestTime) {
+ if (requestTime.length != 1) {
+ throw new IllegalArgumentException("Request time must be specified.");
+ }
+ if (name1.equals(name2)) {
+ return true;
+ }
+
+ if (!hasRole(name1) || !hasRole(name2)) {
+ return false;
+ }
+
+ SessionRole role1 = createRole(name1);
+ return role1.hasValidSession(name2, maxHierarchyLevel, requestTime[0]);
+ }
+
+ /**
+ * Gets all roles that a role inherits at a specific time.
+ *
+ * @param name The name of the role.
+ * @param currentTime The current time to check the role inheritance.
+ * @return A list of roles that the role inherits at the given time.
+ */
+ @Override
+ public List getRoles(String name, String... currentTime) {
+ if (currentTime.length != 1) {
+ throw new IllegalArgumentException("Current time must be specified.");
+ }
+ String requestTime = currentTime[0];
+
+ if (!hasRole(name)) {
+ throw new IllegalArgumentException("Role not found: " + name);
+ }
+
+ return createRole(name).getSessionRoles(requestTime);
+ }
+
+ /**
+ * Gets all users that inherit a specific role at a given time.
+ *
+ * @param name The name of the role to check.
+ * @param currentTime The current time to check the role inheritance.
+ * @return A list of users that inherit the given role at the specified time.
+ */
+ @Override
+ public List getUsers(String name, String... currentTime) {
+ if (currentTime.length != 1) {
+ throw new IllegalArgumentException("Current time must be specified.");
+ }
+ String requestTime = currentTime[0];
+
+ List users = new ArrayList<>();
+ for (SessionRole role : allRoles.values()) {
+ if (role.hasDirectRole(name, requestTime)) {
+ users.add(role.getName());
+ }
+ }
+ Collections.sort(users);
+ return users;
+ }
+
+ /**
+ * Prints all the roles and their sessions.
+ */
+ @Override
+ public void printRoles() {
+ allRoles.values().forEach(role -> System.out.println(role.toString()));
+ }
+}
diff --git a/src/test/java/org/casbin/rolemanager/SessionRoleManagerTest.java b/src/test/java/org/casbin/rolemanager/SessionRoleManagerTest.java
new file mode 100644
index 0000000..9fd3f17
--- /dev/null
+++ b/src/test/java/org/casbin/rolemanager/SessionRoleManagerTest.java
@@ -0,0 +1,219 @@
+// Copyright 2024 The casbin Authors. All Rights Reserved.
+//
+// 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 org.casbin.rolemanager;
+
+import org.casbin.jcasbin.main.Enforcer;
+import org.casbin.jcasbin.persist.file_adapter.FileAdapter;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class SessionRoleManagerTest {
+
+ private SessionRoleManager rm;
+
+ @Before
+ public void setUp() {
+ // Initialize the role manager with a hierarchy level (3 here for example)
+ rm = new SessionRoleManager(3);
+ }
+
+ // Helper functions to simulate time functions from the Go version
+ private String getCurrentTime() {
+ return String.valueOf(System.currentTimeMillis());
+ }
+
+ private String getInOneHour() {
+ return String.valueOf(System.currentTimeMillis() + 3600 * 1000);
+ }
+
+ private String getOneHourAgo() {
+ return String.valueOf(System.currentTimeMillis() - 3600 * 1000);
+ }
+
+ private String getAfterOneHour() {
+ return String.valueOf(System.currentTimeMillis() + 3600 * 1000 + 1);
+ }
+
+ // Test the role inheritance with time-limited sessions
+ @Test
+ public void testSessionRole() {
+ rm.addLink("alpha", "bravo", getCurrentTime(), getInOneHour());
+ rm.addLink("alpha", "charlie", getCurrentTime(), getInOneHour());
+ rm.addLink("bravo", "delta", getCurrentTime(), getInOneHour());
+ rm.addLink("bravo", "echo", getCurrentTime(), getInOneHour());
+ rm.addLink("charlie", "echo", getCurrentTime(), getInOneHour());
+ rm.addLink("charlie", "foxtrott", getCurrentTime(), getInOneHour());
+
+ assertTrue(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ assertTrue(rm.hasLink("alpha", "charlie", getCurrentTime()));
+ assertTrue(rm.hasLink("bravo", "delta", getCurrentTime()));
+ assertTrue(rm.hasLink("bravo", "echo", getCurrentTime()));
+ assertTrue(rm.hasLink("charlie", "echo", getCurrentTime()));
+ assertTrue(rm.hasLink("charlie", "foxtrott", getCurrentTime()));
+
+ assertFalse(rm.hasLink("alpha", "bravo", getOneHourAgo()));
+ assertFalse(rm.hasLink("alpha", "charlie", getOneHourAgo()));
+ assertFalse(rm.hasLink("bravo", "delta", getOneHourAgo()));
+ assertFalse(rm.hasLink("bravo", "echo", getOneHourAgo()));
+ assertFalse(rm.hasLink("charlie", "echo", getOneHourAgo()));
+ assertFalse(rm.hasLink("charlie", "foxtrott", getOneHourAgo()));
+
+ assertFalse(rm.hasLink("alpha", "bravo", getAfterOneHour()));
+ assertFalse(rm.hasLink("alpha", "charlie", getAfterOneHour()));
+ assertFalse(rm.hasLink("bravo", "delta", getAfterOneHour()));
+ assertFalse(rm.hasLink("bravo", "echo", getAfterOneHour()));
+ assertFalse(rm.hasLink("charlie", "echo", getAfterOneHour()));
+ assertFalse(rm.hasLink("charlie", "foxtrott", getAfterOneHour()));
+ }
+
+ // Test the Clear function to ensure all roles are cleared
+ @Test
+ public void testClear() {
+ rm.addLink("alpha", "bravo", getCurrentTime(), getInOneHour());
+ rm.addLink("alpha", "charlie", getCurrentTime(), getInOneHour());
+ rm.clear();
+
+ assertFalse(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ assertFalse(rm.hasLink("alpha", "charlie", getCurrentTime()));
+ }
+
+ @Test
+ public void testAddLink() {
+ rm.addLink("alpha", "bravo", getCurrentTime(), getInOneHour());
+ assertTrue(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ }
+
+ // Test hasLink functionality for session roles
+ @Test
+ public void testHasLink() {
+ assertFalse(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ assertTrue(rm.hasLink("alpha", "alpha", getCurrentTime()));
+
+ rm.addLink("alpha", "bravo", getCurrentTime(), getInOneHour());
+ assertTrue(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ }
+
+ // Test deleting specific links
+ @Test
+ public void testDeleteLink() {
+ rm.addLink("alpha", "bravo", getOneHourAgo(), getInOneHour());
+ rm.deleteLink("alpha", "bravo");
+
+ assertFalse(rm.hasLink("alpha", "bravo", getCurrentTime()));
+ }
+
+ // Test hierarchy level limits
+ @Test
+ public void testHierarchieLevel() {
+ rm = new SessionRoleManager(2);
+ rm.addLink("alpha", "bravo", getOneHourAgo(), getInOneHour());
+ rm.addLink("bravo", "charlie", getOneHourAgo(), getInOneHour());
+
+ assertFalse(rm.hasLink("alpha", "charlie", getCurrentTime()));
+ rm = new SessionRoleManager(3);
+ rm.clear();
+ rm.addLink("alpha", "bravo", getOneHourAgo(), getInOneHour());
+ rm.addLink("bravo", "charlie", getOneHourAgo(), getInOneHour());
+
+ assertTrue(rm.hasLink("alpha", "charlie", getCurrentTime()));
+ }
+
+ // Test expired session handling
+ @Test
+ public void testOutdatedSessions() {
+ rm.addLink("alpha", "bravo", getOneHourAgo(), getCurrentTime());
+ rm.addLink("bravo", "charlie", getOneHourAgo(), getInOneHour());
+
+ assertFalse(rm.hasLink("alpha", "bravo", getInOneHour()));
+ assertTrue(rm.hasLink("alpha", "charlie", getOneHourAgo()));
+ }
+
+ // Test role inheritance
+ @Test
+ public void testGetRoles() {
+ String oneHourAgo = getOneHourAgo();
+ String currentTime = getCurrentTime();
+ String inOneHour = getInOneHour();
+
+ rm.addLink("alpha", "bravo", oneHourAgo, inOneHour);
+ rm.addLink("alpha", "charlie", oneHourAgo, currentTime);
+
+ assertEquals(Arrays.asList("bravo", "charlie"), rm.getRoles("alpha", oneHourAgo));
+ assertEquals(Arrays.asList("bravo"), rm.getRoles("alpha", currentTime + 1));
+ }
+
+ // Test user-role mappings
+ @Test
+ public void testGetUsers() {
+ rm.addLink("bravo", "alpha", getOneHourAgo(), getInOneHour());
+ rm.addLink("charlie", "alpha", getOneHourAgo(), getInOneHour());
+ rm.addLink("delta", "alpha", getOneHourAgo(), getInOneHour());
+
+ assertEquals(Arrays.asList("bravo", "charlie", "delta"), rm.getUsers("alpha", getCurrentTime()));
+ }
+
+ // Test the Enforcer with session-based roles and policies
+ @Test
+ public void testEnforcer() throws Exception {
+ // Create a new Enforcer using the model path. The default role manager is used initially.
+ Enforcer e = new Enforcer("examples/rbac_model_with_sessions.conf");
+
+ // Manually set an adapter for the policy.
+ FileAdapter a = new FileAdapter("examples/rbac_policy_with_sessions.csv");
+ e.setAdapter(a);
+
+ // Use our custom role manager.
+ SessionRoleManager rm = new SessionRoleManager(10);
+ e.setRoleManager(rm);
+
+ // If our role manager relies on Casbin policy (e.g., reading "g" policy rules),
+ // we need to set the role manager before loading the policy.
+ e.loadPolicy();
+
+ // Current role inheritance tree (Time ranges shown in parentheses):
+ // delta echo foxtrott
+ // \ / \ /
+ // (0-20) \ (5-15) / \ (10-20) / (10-12)
+ // \ / \ /
+ // bravo charlie
+ // \ /
+ // (0-10) \ / (5-15)
+ // \ /
+ // alpha
+
+ // Test permissions for different time points
+ assertTrue(e.enforce("alpha", "data1", "read", "00"));
+ assertTrue(e.enforce("alpha", "data1", "read", "05"));
+ assertTrue(e.enforce("alpha", "data1", "read", "10"));
+ assertFalse(e.enforce("alpha", "data1", "read", "15"));
+ assertFalse(e.enforce("alpha", "data1", "read", "20"));
+
+ assertFalse(e.enforce("alpha", "data2", "read", "00"));
+ assertTrue(e.enforce("alpha", "data2", "read", "05"));
+ assertTrue(e.enforce("alpha", "data2", "read", "10"));
+ assertTrue(e.enforce("alpha", "data2", "read", "15"));
+ assertFalse(e.enforce("alpha", "data2", "read", "20"));
+
+ assertFalse(e.enforce("alpha", "data3", "read", "00"));
+ assertFalse(e.enforce("alpha", "data3", "read", "05"));
+ assertTrue(e.enforce("alpha", "data3", "read", "10"));
+ assertFalse(e.enforce("alpha", "data3", "read", "15"));
+ assertFalse(e.enforce("alpha", "data3", "read", "20"));
+ }
+}