Skip to content

Commit fcba221

Browse files
author
Asaf Cohen
authored
Merge pull request #10 from permitio/asaf/per-6030-java-improve-test-coverage-and-logging
Fix condition set rule create + add more tests
2 parents 1a335ae + 7f7a36f commit fcba221

26 files changed

Lines changed: 1104 additions & 237 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.permit.sdk;
2+
3+
public enum ApiContextLevel {
4+
WAIT_FOR_INIT(0),
5+
ORGANIZATION(1),
6+
PROJECT(2),
7+
ENVIRONMENT(3);
8+
9+
private final int value;
10+
11+
ApiContextLevel(int value) {
12+
this.value = value;
13+
}
14+
15+
public int getValue() {
16+
return value;
17+
}
18+
}
Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
package io.permit.sdk;
22

33
/**
4-
* The {@code ApiKeyLevel} enum represents the different levels (or scopes) of API keys in the Permit SDK.
5-
* These levels determine the scope of permissions granted by the API key: is the API granting permission
4+
* The {@code ApiKeyLevel} enum represents the granted access level of an API key used by the Permit SDK.
5+
* The access level determine the scope of permissions granted by the API key: is the API granting permission
66
* to the entire workspace (i.e: organization), to a specific project within the workspace, or to a specific
77
* project and environment within the workspace.
8+
*
9+
* NOTE: Access levels are enforced on the backend using the API key (you cannot override this value).
10+
* this enum is intended as a read-only representation to help the user understand his access level from code.
811
*/
912
public enum ApiKeyLevel {
10-
WAIT_FOR_INIT,
11-
ORGANIZATION_LEVEL_API_KEY,
12-
PROJECT_LEVEL_API_KEY,
13-
ENVIRONMENT_LEVEL_API_KEY,
13+
WAIT_FOR_INIT(0),
14+
ORGANIZATION_LEVEL_API_KEY(1),
15+
PROJECT_LEVEL_API_KEY(2),
16+
ENVIRONMENT_LEVEL_API_KEY(3);
17+
18+
private final int value;
19+
20+
ApiKeyLevel(int value) {
21+
this.value = value;
22+
}
23+
24+
public int getValue() {
25+
return value;
26+
}
1427
}

src/main/java/io/permit/sdk/PermitConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public class PermitConfig {
2626
private final String defaultTenant;
2727
private final Boolean useDefaultTenantIfEmpty;
2828
private PermitContext context;
29+
public final String version;
30+
private final static String defaultVersion = "1.3.1-rc";
2931

3032
private PermitConfig(Builder builder) {
3133
this.token = builder.token;
@@ -41,6 +43,8 @@ private PermitConfig(Builder builder) {
4143
this.defaultTenant = builder.defaultTenant;
4244
this.useDefaultTenantIfEmpty = builder.useDefaultTenantIfEmpty;
4345
this.context = builder.context;
46+
String runtimeVersion = Permit.class.getPackage().getImplementationVersion();
47+
this.version = (runtimeVersion == null) ? defaultVersion : runtimeVersion;
4448
}
4549

4650
/**

src/main/java/io/permit/sdk/PermitContext.java

Lines changed: 168 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,116 @@
11
package io.permit.sdk;
22

3+
import io.permit.sdk.api.PermitContextChangeError;
4+
35
/**
46
* The {@code PermitContext} class represents the context for Permit API calls.
5-
*
6-
* A context can be: the entire workspace, a specific project, or a specific environment.
7-
* Most API methods require an environment scope (e.g: in order to create a role you
8-
* need to know in which environment to place it), but some methods can work in higher
9-
* scopes (for example: creating a project can be done with a workspace-level scope).
7+
*
8+
* Since the Permit API hierarchy is deeply nested, it is less convenient to specify
9+
* the full object hierarchy in every request.
10+
*
11+
* For example, in order to list roles, the user needs to specify the (id or key) of the:
12+
* - the org
13+
* - the project
14+
* - then environment
15+
* in which the roles are located under.
16+
*
17+
* Instead, the SDK can "remember" the current context and "auto-complete" the details
18+
* from that context.
19+
*
20+
* We then get this kind of experience:
21+
* <pre>{@code
22+
* permit.api.roles.list();
23+
* }</pre>
24+
*
25+
* We can only run this function if the current context already knows the org, project,
26+
* and environment that we want to run under, and that is why in this example the api
27+
* method assumes we are running under an {@link ApiContextLevel#ENVIRONMENT} context.
1028
*/
1129
public class PermitContext {
12-
private final ApiKeyLevel apiKeyLevel;
13-
private final String org;
14-
private final String project;
15-
private final String environment;
30+
private ApiKeyLevel apiKeyLevel;
31+
private String permittedOrganization;
32+
private String permittedProject;
33+
private String permittedEnviroment;
34+
35+
private ApiContextLevel contextLevel;
36+
private String org;
37+
private String project;
38+
private String environment;
1639

1740
/**
1841
* Constructs a new instance of the {@code PermitContext} class with the specified builder.
1942
*
2043
* @param builder The builder used to construct the PermitContext.
2144
*/
2245
public PermitContext(Builder builder) {
23-
this.apiKeyLevel = builder.apiKeyLevel;
46+
this.saveApiKeyAccessibleScope(builder.org, builder.project, builder.environment);
47+
this.contextLevel = builder.contextLevel;
2448
this.org = builder.org;
2549
this.project = builder.project;
2650
this.environment = builder.environment;
2751
}
2852

53+
public PermitContext() {
54+
// access level
55+
this.apiKeyLevel = ApiKeyLevel.WAIT_FOR_INIT;
56+
this.permittedOrganization = null;
57+
this.permittedProject = null;
58+
this.permittedEnviroment = null;
59+
60+
// known context
61+
this.contextLevel = ApiContextLevel.WAIT_FOR_INIT;
62+
this.org = null;
63+
this.project = null;
64+
this.environment = null;
65+
}
66+
67+
private void saveApiKeyAccessibleScope(String org, String project, String environment) {
68+
// Do not call this method directly!
69+
permittedOrganization = org; // cannot be null
70+
71+
if (project != null && environment != null) {
72+
permittedProject = project;
73+
permittedEnviroment = environment;
74+
apiKeyLevel = ApiKeyLevel.ENVIRONMENT_LEVEL_API_KEY;
75+
} else if (project != null) {
76+
permittedProject = project;
77+
permittedEnviroment = null;
78+
apiKeyLevel = ApiKeyLevel.PROJECT_LEVEL_API_KEY;
79+
} else {
80+
permittedProject = null;
81+
permittedEnviroment = null;
82+
apiKeyLevel = ApiKeyLevel.ORGANIZATION_LEVEL_API_KEY;
83+
}
84+
}
85+
2986
/**
30-
* Returns the API key level associated with the Permit context.
87+
* Returns the access level of the API key used by the SDK.
3188
*
32-
* @return The API key level.
89+
* @deprecated replaced with {@link PermitContext#getPermittedAccessLevel}
90+
* @return The API key access level.
3391
*/
3492
public ApiKeyLevel getApiKeyLevel() {
3593
return apiKeyLevel;
3694
}
3795

96+
/**
97+
* Returns the access level of the API key used by the SDK.
98+
*
99+
* @return The API key access level.
100+
*/
101+
public ApiKeyLevel getPermittedAccessLevel() {
102+
return apiKeyLevel;
103+
}
104+
105+
/**
106+
* Returns the current SDK context level.
107+
*
108+
* @return the context level.
109+
*/
110+
public ApiContextLevel getContextLevel() {
111+
return contextLevel;
112+
}
113+
38114
/**
39115
* Returns the organization (workspace) associated with the Permit context.
40116
*
@@ -62,11 +138,86 @@ public String getEnvironment() {
62138
return environment;
63139
}
64140

141+
/**
142+
* Sets the current context of the SDK to a specific organization.
143+
*
144+
* @param org The organization uuid.
145+
* @throws PermitContextChangeError If the SDK context cannot be set due to insufficient API Key permissions.
146+
*/
147+
public void setOrganizationLevelContext(String org) throws PermitContextChangeError {
148+
verifyCanAccessOrg(org);
149+
this.contextLevel = ApiContextLevel.ORGANIZATION;
150+
this.org = org;
151+
this.project = null;
152+
this.environment = null;
153+
}
154+
155+
/**
156+
* Sets the current context of the SDK to a specific project within an organization.
157+
*
158+
* @param org The organization uuid.
159+
* @param project The project uuid.
160+
* @throws PermitContextChangeError If the SDK context cannot be set due to insufficient API Key permissions.
161+
*/
162+
public void setProjectLevelContext(String org, String project) throws PermitContextChangeError {
163+
verifyCanAccessProject(org, project);
164+
this.contextLevel = ApiContextLevel.PROJECT;
165+
this.org = org;
166+
this.project = project;
167+
this.environment = null;
168+
}
169+
170+
/**
171+
* Sets the current context of the SDK to a specific environment within an organization and project.
172+
*
173+
* @param org The organization uuid.
174+
* @param project The project uuid.
175+
* @param environment The environment uuid.
176+
* @throws PermitContextChangeError If the SDK context cannot be set due to insufficient API Key permissions.
177+
*/
178+
public void setEnvironmentLevelContext(String org, String project, String environment) throws PermitContextChangeError {
179+
verifyCanAccessEnvironment(org, project, environment);
180+
this.contextLevel = ApiContextLevel.ENVIRONMENT;
181+
this.org = org;
182+
this.project = project;
183+
this.environment = environment;
184+
}
185+
186+
private void verifyCanAccessOrg(String org) throws PermitContextChangeError {
187+
if (!org.equals(permittedOrganization)) {
188+
throw new PermitContextChangeError(
189+
"You cannot set an SDK context with org '" + org +
190+
"' due to insufficient API Key permissions"
191+
);
192+
}
193+
}
194+
195+
private void verifyCanAccessProject(String org, String project) throws PermitContextChangeError {
196+
verifyCanAccessOrg(org);
197+
if (permittedProject != null && !project.equals(permittedProject)) {
198+
throw new PermitContextChangeError(
199+
"You cannot set an SDK context with project '" + project +
200+
"' due to insufficient API Key permissions"
201+
);
202+
}
203+
}
204+
205+
private void verifyCanAccessEnvironment(String org, String project, String environment) throws PermitContextChangeError {
206+
verifyCanAccessProject(org, project);
207+
if (permittedEnviroment != null && !environment.equals(permittedEnviroment)) {
208+
throw new PermitContextChangeError(
209+
"You cannot set an SDK context with environment '" + environment +
210+
"' due to insufficient API Key permissions"
211+
);
212+
}
213+
}
214+
65215
/**
66216
* The {@code Builder} class provides a builder interface for constructing {@code PermitContext} objects.
67217
*/
68218
public static class Builder {
69-
private ApiKeyLevel apiKeyLevel;
219+
220+
private ApiContextLevel contextLevel;
70221
private String org;
71222
private String project;
72223
private String environment;
@@ -78,7 +229,7 @@ public static class Builder {
78229
* context will be stored in the SDK configuration for future API calls.
79230
*/
80231
public Builder() {
81-
this.apiKeyLevel = ApiKeyLevel.WAIT_FOR_INIT;
232+
this.contextLevel = ApiContextLevel.WAIT_FOR_INIT;
82233
this.org = null;
83234
this.project = null;
84235
this.environment = null;
@@ -93,10 +244,10 @@ public Builder() {
93244
* @return The updated {@code Builder} object.
94245
*/
95246
public Builder withOrganization(String org) {
247+
this.contextLevel = ApiContextLevel.ORGANIZATION;
96248
this.org = org;
97249
this.project = null;
98250
this.environment = null;
99-
this.apiKeyLevel = ApiKeyLevel.ORGANIZATION_LEVEL_API_KEY;
100251
return this;
101252
}
102253

@@ -110,10 +261,10 @@ public Builder withOrganization(String org) {
110261
* @return The updated {@code Builder} object.
111262
*/
112263
public Builder withProject(String org, String project) {
264+
this.contextLevel = ApiContextLevel.PROJECT;
113265
this.org = org;
114266
this.project = project;
115267
this.environment = null;
116-
this.apiKeyLevel = ApiKeyLevel.PROJECT_LEVEL_API_KEY;
117268
return this;
118269
}
119270

@@ -127,10 +278,10 @@ public Builder withProject(String org, String project) {
127278
* @return The updated {@code Builder} object.
128279
*/
129280
public Builder withEnvironment(String org, String project, String environment) {
281+
this.contextLevel = ApiContextLevel.ENVIRONMENT;
130282
this.org = org;
131283
this.project = project;
132284
this.environment = environment;
133-
this.apiKeyLevel = ApiKeyLevel.ENVIRONMENT_LEVEL_API_KEY;
134285
return this;
135286
}
136287

0 commit comments

Comments
 (0)