-
Notifications
You must be signed in to change notification settings - Fork 227
/
CheckExpression.java
141 lines (119 loc) · 5.08 KB
/
CheckExpression.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
* Copyright 2017, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.core.security.permissions.expressions;
import static com.yahoo.elide.core.security.permissions.ExpressionResult.DEFERRED;
import static com.yahoo.elide.core.security.permissions.ExpressionResult.FAIL;
import static com.yahoo.elide.core.security.permissions.ExpressionResult.PASS;
import static com.yahoo.elide.core.security.permissions.ExpressionResult.UNEVALUATED;
import com.yahoo.elide.core.PersistentResource;
import com.yahoo.elide.core.dictionary.EntityDictionary;
import com.yahoo.elide.core.security.ChangeSpec;
import com.yahoo.elide.core.security.RequestScope;
import com.yahoo.elide.core.security.checks.Check;
import com.yahoo.elide.core.security.checks.OperationCheck;
import com.yahoo.elide.core.security.checks.UserCheck;
import com.yahoo.elide.core.security.permissions.ExpressionResult;
import com.yahoo.elide.core.security.permissions.ExpressionResultCache;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
/**
* An expression in the security evaluation AST that wraps an actual check.
*/
@Slf4j
public class CheckExpression implements Expression {
@Getter
protected final Check check;
protected final PersistentResource resource;
protected final RequestScope requestScope;
protected final ExpressionResultCache cache;
protected ExpressionResult result;
private final Optional<ChangeSpec> changeSpec;
/**
* Constructor.
*
* @param check The check to be evaluated by this expression
* @param resource The resource to pass to the check
* @param requestScope The requestScope to pass to the check
* @param changeSpec The changeSpec to pass to the check
* @param cache The cache of previous expression results
*/
public CheckExpression(final Check check,
final PersistentResource resource,
final RequestScope requestScope,
final ChangeSpec changeSpec,
final ExpressionResultCache cache) {
this.check = check;
this.requestScope = requestScope;
this.changeSpec = Optional.ofNullable(changeSpec);
this.cache = cache;
this.result = UNEVALUATED;
// UserCheck does not use resource
this.resource = (check instanceof UserCheck) ? null : resource;
}
@Override
public ExpressionResult evaluate(EvaluationMode mode) {
log.trace("Evaluating check: {} in mode {}", check, mode);
/* Result evaluation is sticky once evaluated to PASS or FAIL */
if (result == PASS || result == FAIL) {
return result;
}
if (mode == EvaluationMode.USER_CHECKS_ONLY && ! (check instanceof UserCheck)) {
result = DEFERRED;
return result;
}
if (mode == EvaluationMode.INLINE_CHECKS_ONLY && (resource != null && resource.isNewlyCreated())) {
result = DEFERRED;
return result;
}
if (mode == EvaluationMode.INLINE_CHECKS_ONLY && check.runAtCommit()) {
result = DEFERRED;
return result;
}
// If we have a valid change spec, do not cache the result or look for a cached result.
if (changeSpec.isPresent()) {
log.trace("-- Check has changespec: {}", changeSpec);
result = computeCheck();
log.trace("-- Check returned with result: {}", result);
return result;
}
// Otherwise, search the cache and use value if found. Otherwise, evaluate and add it to the cache.
log.trace("-- Check does NOT have changespec");
Class<? extends Check> checkClass = check.getClass();
if (cache.hasStoredResultFor(checkClass, resource)) {
result = cache.getResultFor(checkClass, resource);
} else {
result = computeCheck();
cache.putResultFor(checkClass, resource, result);
log.trace("-- Check computed result: {}", result);
}
log.trace("-- Check returned with result: {}", result);
return result;
}
@Override
public <T> T accept(ExpressionVisitor<T> visitor) {
return visitor.visitCheckExpression(this);
}
/**
* Actually compute the result of the check without caching concerns.
*
* @return Expression result from the check.
*/
private ExpressionResult computeCheck() {
Object entity = (resource == null) ? null : resource.getObject();
if (check instanceof UserCheck) {
result = ((UserCheck) check).ok(requestScope.getUser()) ? PASS : FAIL;
} else {
result = ((OperationCheck) check).ok(entity, requestScope, changeSpec) ? PASS : FAIL;
}
return result;
}
@Override
public String toString() {
EntityDictionary dictionary = ((com.yahoo.elide.core.RequestScope) requestScope).getDictionary();
return String.format("(%s %s)", dictionary.getCheckIdentifier(check.getClass()), result);
}
}