Skip to content

Commit 14da0a3

Browse files
muditchaudharyjtbrain
authored andcommitted
Creates Backward Compatible Context Class (cedar-policy#286)
Signed-off-by: Mudit Chaudhary <chmudit@amazon.com>
1 parent 4943f87 commit 14da0a3

File tree

6 files changed

+511
-1
lines changed

6 files changed

+511
-1
lines changed

CedarJava/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
.factorypath
1414
.project
1515
.settings/
16-
16+
bin/
1717
# Ignore changes to gradle.properties because we enter passwords here for releases
1818
/gradle.properties

CedarJava/src/main/java/com/cedarpolicy/model/AuthorizationRequest.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ public AuthorizationRequest(
9595
this.enableRequestValidation = enableRequestValidation;
9696
}
9797

98+
/**
99+
* Create an authorization request from the EUIDs and Context.
100+
* Constructor overloading to support Context object while preserving backward compatability.
101+
*
102+
* @param principalEUID Principal's EUID.
103+
* @param actionEUID Action's EUID.
104+
* @param resourceEUID Resource's EUID.
105+
* @param context Context object.
106+
* @param schema Schema (optional).
107+
* @param enableRequestValidation Whether to use the schema for just
108+
* schema-based parsing of `context` (false) or also for request validation
109+
* (true). No effect if `schema` is not provided.
110+
*/
111+
public AuthorizationRequest(
112+
EntityUID principalEUID,
113+
EntityUID actionEUID,
114+
EntityUID resourceEUID,
115+
Context context,
116+
Optional<Schema> schema,
117+
boolean enableRequestValidation) {
118+
this.principalEUID = principalEUID;
119+
this.actionEUID = actionEUID;
120+
this.resourceEUID = resourceEUID;
121+
this.context = Optional.of(context.getContext());
122+
this.schema = schema;
123+
this.enableRequestValidation = enableRequestValidation;
124+
}
125+
98126
/**
99127
* Create a request without a schema.
100128
*
@@ -113,6 +141,25 @@ public AuthorizationRequest(EntityUID principalEUID, EntityUID actionEUID, Entit
113141
false);
114142
}
115143

144+
/**
145+
* Create a request without a schema.
146+
* Constructor overloading to support Context object while preserving backward compatability.
147+
*
148+
* @param principalEUID Principal's EUID.
149+
* @param actionEUID Action's EUID.
150+
* @param resourceEUID Resource's EUID.
151+
* @param context Key/Value context.
152+
*/
153+
public AuthorizationRequest(EntityUID principalEUID, EntityUID actionEUID, EntityUID resourceEUID, Context context) {
154+
this(
155+
principalEUID,
156+
actionEUID,
157+
resourceEUID,
158+
context,
159+
Optional.empty(),
160+
false);
161+
}
162+
116163
/**
117164
* Create a request without a schema, using Entity objects for principal/action/resource.
118165
*
@@ -129,6 +176,24 @@ public AuthorizationRequest(Entity principalEUID, Entity actionEUID, Entity reso
129176
context);
130177
}
131178

179+
/**
180+
* Create a request without a schema, using Entity objects for principal/action/resource.
181+
* Constructor overloading to support Context object while preserving backward compatability.
182+
*
183+
* @param principalEUID Principal's EUID.
184+
* @param actionEUID Action's EUID.
185+
* @param resourceEUID Resource's EUID.
186+
* @param context Key/Value context.
187+
*/
188+
public AuthorizationRequest(Entity principalEUID, Entity actionEUID, Entity resourceEUID, Context context) {
189+
this(
190+
principalEUID.getEUID(),
191+
actionEUID.getEUID(),
192+
resourceEUID.getEUID(),
193+
context);
194+
}
195+
196+
132197
/**
133198
* Create a request from Entity objects and Context.
134199
*
@@ -154,6 +219,31 @@ public AuthorizationRequest(Entity principal, Entity action, Entity resource,
154219
);
155220
}
156221

222+
/**
223+
* Create a request from Entity objects and Context.
224+
* Constructor overloading to support Context object while preserving backward compatability.
225+
*
226+
* @param principal
227+
* @param action
228+
* @param resource
229+
* @param context
230+
* @param schema
231+
* @param enableRequestValidation Whether to use the schema for just
232+
* schema-based parsing of `context` (false) or also for request validation
233+
* (true). No effect if `schema` is not provided.
234+
*/
235+
236+
public AuthorizationRequest(Entity principal, Entity action, Entity resource,
237+
Context context, Optional<Schema> schema, boolean enableRequestValidation) {
238+
this(
239+
principal.getEUID(),
240+
action.getEUID(),
241+
resource.getEUID(),
242+
context,
243+
schema,
244+
enableRequestValidation
245+
);
246+
}
157247

158248
/** Readable string representation. */
159249
@Override
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright Cedar Contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.cedarpolicy.model;
18+
19+
import java.util.HashMap;
20+
import java.util.Collections;
21+
import java.util.stream.Collectors;
22+
import java.util.stream.StreamSupport;
23+
import java.util.Map;
24+
import com.cedarpolicy.value.Value;
25+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
26+
27+
public class Context {
28+
29+
private Map<String, Value> context;
30+
31+
/**
32+
* Constructs a new empty Context with no key-value pairs. Initializes the internal context map as an empty
33+
* immutable map.
34+
*/
35+
public Context() {
36+
context = Collections.emptyMap();
37+
}
38+
39+
public boolean isEmpty() {
40+
return context.isEmpty();
41+
}
42+
43+
/**
44+
* Constructs a new Context from an Iterable of key-value pairs. Creates a new HashMap and populates it with the
45+
* provided entries. Equivalent to from_pairs in Cedar Rust.
46+
*
47+
* @param contextList An Iterable containing key-value pairs to initialize this context with
48+
* @throws IllegalStateException if a duplicate key is found within the iterable
49+
* @throws IllegalArgumentException if the contextList parameter is null
50+
*/
51+
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
52+
public Context(Iterable<Map.Entry<String, Value>> contextList) {
53+
context = new HashMap<>();
54+
mergeContextFromIterable(contextList);
55+
}
56+
57+
/**
58+
* Constructs a new Context with the provided map of key-value pairs. Creates a defensive copy of the input map to
59+
* maintain immutability.
60+
*
61+
* @param contextMap The map of key-value pairs to initialize this context with
62+
* @throws IllegalArgumentException if the contextMap parameter is null
63+
*/
64+
public Context(Map<String, Value> contextMap) {
65+
context = new HashMap<>();
66+
context.putAll(contextMap);
67+
}
68+
69+
/**
70+
* Returns a defensive copy of the internal context map.
71+
*
72+
* @return A new HashMap containing all key-value pairs from the internal context
73+
*/
74+
public Map<String, Value> getContext() {
75+
return new HashMap<>(context);
76+
}
77+
78+
/**
79+
* Merges another Context object into the current context.
80+
*
81+
* @param contextToMerge The Context object to merge into this context
82+
* @throws IllegalStateException if a duplicate key is found while merging the context
83+
* @throws IllegalArgumentException if the contextToMerge parameter is null
84+
*/
85+
public void merge(Context contextToMerge) throws IllegalStateException, IllegalArgumentException {
86+
mergeContextFromIterable(contextToMerge.getContext().entrySet());
87+
}
88+
89+
/**
90+
* Merges the provided key-value pairs into the current context.
91+
*
92+
* @param contextMaps An Iterable containing key-value pairs to merge into this context
93+
* @throws IllegalStateException if a duplicate key is found in the existing context or duplicate key found
94+
* within the iterable
95+
* @throws IllegalArgumentException if the contextMaps parameter is null
96+
*/
97+
public void merge(Iterable<Map.Entry<String, Value>> contextMaps)
98+
throws IllegalStateException, IllegalArgumentException {
99+
mergeContextFromIterable(contextMaps);
100+
}
101+
102+
/**
103+
* Retrieves the Value associated with the specified key from the context.
104+
*
105+
* @param key The key whose associated Value is to be returned
106+
* @return The Value associated with the specified key, or null if the key is not found replicating Cedar Rust
107+
* behavior
108+
* @throws IllegalArgumentException if the key parameter is null
109+
*/
110+
public Value get(String key) {
111+
if (key == null) {
112+
throw new IllegalArgumentException("Key cannot be null");
113+
}
114+
return context.getOrDefault(key, null);
115+
}
116+
117+
/**
118+
* Processes an Iterable of Map entries and adds them to the context.
119+
*
120+
* @param contextIterator The Iterable containing key-value pairs to add to the context
121+
* @throws IllegalStateException if a duplicate key is found in the existing context or duplicate key found
122+
* within the iterable
123+
* @throws IllegalArgumentException if the contextIterator is null
124+
*/
125+
private void mergeContextFromIterable(Iterable<Map.Entry<String, Value>> contextIterator)
126+
throws IllegalStateException, IllegalArgumentException {
127+
if (contextIterator == null) {
128+
throw new IllegalArgumentException("Context iterator cannot be null");
129+
}
130+
131+
Map<String, Value> newEntries = StreamSupport.stream(contextIterator.spliterator(), false).peek(entry -> {
132+
if (context.containsKey(entry.getKey())) {
133+
throw new IllegalStateException(
134+
String.format("Duplicate key '%s' in existing context", entry.getKey()));
135+
}
136+
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
137+
context.putAll(newEntries);
138+
}
139+
140+
/** Readable string representation. */
141+
@Override
142+
public String toString() {
143+
return context.toString();
144+
}
145+
}

CedarJava/src/main/java/com/cedarpolicy/model/PartialAuthorizationRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ public Builder context(Map<String, Value> context) {
160160
return this;
161161
}
162162

163+
public Builder context(Context context) {
164+
this.context = Optional.of(ImmutableMap.copyOf(context.getContext()));
165+
return this;
166+
}
167+
163168
/**
164169
* Set the context to be empty, not unknown
165170
* @return The builder.

0 commit comments

Comments
 (0)