0
# Service Access Control
1
2
Service-level access control strategies for surrogate authentication enable fine-grained authorization at the individual CAS service level. These strategies determine whether surrogate authentication is permitted for specific services and provide different authorization mechanisms including attribute-based and script-based control.
3
4
## Capabilities
5
6
### Base Access Strategy
7
8
Abstract base class providing common functionality for surrogate-aware service access strategies.
9
10
```java { .api }
11
public abstract class BaseSurrogateRegisteredServiceAccessStrategy extends BaseRegisteredServiceAccessStrategy {
12
13
/**
14
* Check if the current request is from a surrogate authentication session.
15
*
16
* @param request the service access strategy request
17
* @return true if this is a surrogate authentication session
18
*/
19
protected boolean isSurrogateAuthenticationSession(RegisteredServiceAccessStrategyRequest request) {
20
return request.getAttributes().containsKey(SurrogateAuthenticationService.AUTHENTICATION_ATTR_SURROGATE_ENABLED);
21
}
22
}
23
```
24
25
### Attribute-Based Access Strategy
26
27
Service access strategy that uses principal attributes to determine surrogate authentication authorization.
28
29
```java { .api }
30
@Getter
31
@Setter
32
@EqualsAndHashCode(callSuper = true)
33
public class SurrogateRegisteredServiceAccessStrategy extends BaseSurrogateRegisteredServiceAccessStrategy {
34
35
/**
36
* Defines the attribute aggregation behavior when checking for required attributes.
37
* Default requires that all attributes be present and match the principal's.
38
*/
39
protected boolean requireAllAttributes = true;
40
41
/**
42
* Indicates whether matching on required attribute values
43
* should be done in a case-insensitive manner.
44
*/
45
protected boolean caseInsensitive;
46
47
/**
48
* Required attributes for surrogate access authorization.
49
* Map of attribute names to sets of required values.
50
*/
51
private Map<String, Set<String>> surrogateRequiredAttributes = new HashMap<>(0);
52
53
/**
54
* Main authorization method for service access requests.
55
*
56
* @param request the service access request
57
* @return true if access is authorized
58
*/
59
@Override
60
public boolean authorizeRequest(RegisteredServiceAccessStrategyRequest request) {
61
return !isSurrogateAuthenticationSession(request) || doPrincipalAttributesAllowSurrogateServiceAccess(request);
62
}
63
64
/**
65
* Evaluate principal attributes against required surrogate attributes.
66
*
67
* @param request the service access request
68
* @return true if attributes meet requirements
69
*/
70
protected boolean doPrincipalAttributesAllowSurrogateServiceAccess(RegisteredServiceAccessStrategyRequest request) {
71
return RegisteredServiceAccessStrategyEvaluator.builder()
72
.caseInsensitive(this.caseInsensitive)
73
.requireAllAttributes(this.requireAllAttributes)
74
.requiredAttributes(this.surrogateRequiredAttributes)
75
.rejectedAttributes(new LinkedHashMap<>(0))
76
.build()
77
.apply(request);
78
}
79
}
80
```
81
82
### Groovy Script-Based Access Strategy
83
84
Service access strategy that uses Groovy scripts for flexible, programmatic authorization logic.
85
86
```java { .api }
87
@Slf4j
88
@Getter
89
@Setter
90
@EqualsAndHashCode(callSuper = true)
91
public class GroovySurrogateRegisteredServiceAccessStrategy extends BaseSurrogateRegisteredServiceAccessStrategy {
92
93
/**
94
* Path to Groovy script for authorization logic.
95
* Supports Spring Expression Language for dynamic script resolution.
96
*/
97
@ExpressionLanguageCapable
98
private String groovyScript;
99
100
/**
101
* Execute Groovy script to determine authorization.
102
*
103
* @param request the service access request
104
* @return true if access is authorized
105
* @throws Throwable if script execution fails
106
*/
107
@Override
108
public boolean authorizeRequest(RegisteredServiceAccessStrategyRequest request) throws Throwable {
109
if (isSurrogateAuthenticationSession(request)) {
110
try {
111
Object[] args = new Object[]{request.getPrincipalId(), request.getAttributes(), LOGGER};
112
Resource resource = ResourceUtils.getResourceFrom(SpringExpressionLanguageValueResolver.getInstance().resolve(this.groovyScript));
113
ExecutableCompiledScriptFactory scriptFactory = ExecutableCompiledScriptFactory.getExecutableCompiledScriptFactory();
114
return scriptFactory.fromResource(resource).execute(args, Boolean.class);
115
} catch (Exception e) {
116
LoggingUtils.error(LOGGER, e);
117
}
118
return false;
119
}
120
return super.authorizeRequest(request);
121
}
122
}
123
```
124
125
## Usage Examples
126
127
### Configuring Attribute-Based Access Control
128
129
```java
130
import org.apereo.cas.services.SurrogateRegisteredServiceAccessStrategy;
131
132
import java.util.*;
133
134
// Create attribute-based access strategy
135
SurrogateRegisteredServiceAccessStrategy strategy = new SurrogateRegisteredServiceAccessStrategy();
136
137
// Configure to require all attributes and use case-insensitive matching
138
strategy.setRequireAllAttributes(true);
139
strategy.setCaseInsensitive(true);
140
141
// Set required attributes for surrogate access
142
Map<String, Set<String>> requiredAttributes = new HashMap<>();
143
requiredAttributes.put("role", Set.of("admin", "support"));
144
requiredAttributes.put("department", Set.of("IT", "Security"));
145
strategy.setSurrogateRequiredAttributes(requiredAttributes);
146
147
// Use in service registry
148
RegisteredService service = // ... create service
149
service.setAccessStrategy(strategy);
150
```
151
152
### Groovy Script Authorization
153
154
```java
155
import org.apereo.cas.services.GroovySurrogateRegisteredServiceAccessStrategy;
156
157
// Create script-based access strategy
158
GroovySurrogateRegisteredServiceAccessStrategy strategy = new GroovySurrogateRegisteredServiceAccessStrategy();
159
160
// Set Groovy script path (supports classpath:, file:, http: URLs)
161
strategy.setGroovyScript("classpath:surrogate-authorization.groovy");
162
163
// Use in service registry
164
RegisteredService service = // ... create service
165
service.setAccessStrategy(strategy);
166
```
167
168
Example Groovy script (`surrogate-authorization.groovy`):
169
170
```groovy
171
// Script receives: principalId, attributes, logger
172
def principalId = args[0]
173
def attributes = args[1]
174
def logger = args[2]
175
176
logger.debug("Evaluating surrogate access for principal: {}", principalId)
177
178
// Check if user has admin role
179
def roles = attributes.get("roles")
180
if (roles != null && roles.contains("admin")) {
181
logger.info("Granting surrogate access to admin user: {}", principalId)
182
return true
183
}
184
185
// Check business hours for support staff
186
def supportRoles = attributes.get("roles")
187
if (supportRoles != null && supportRoles.contains("support")) {
188
def hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
189
def isBusinessHours = hour >= 9 && hour <= 17
190
191
if (isBusinessHours) {
192
logger.info("Granting surrogate access to support user during business hours: {}", principalId)
193
return true
194
}
195
}
196
197
logger.warn("Denying surrogate access for principal: {}", principalId)
198
return false
199
```
200
201
### Custom Access Strategy Implementation
202
203
```java
204
import org.apereo.cas.services.BaseSurrogateRegisteredServiceAccessStrategy;
205
import org.apereo.cas.services.RegisteredServiceAccessStrategyRequest;
206
207
public class TimeBoundSurrogateAccessStrategy extends BaseSurrogateRegisteredServiceAccessStrategy {
208
209
private int startHour = 9;
210
private int endHour = 17;
211
212
@Override
213
public boolean authorizeRequest(RegisteredServiceAccessStrategyRequest request) {
214
if (!isSurrogateAuthenticationSession(request)) {
215
return true; // Non-surrogate sessions are always allowed
216
}
217
218
// Check time bounds for surrogate sessions
219
Calendar now = Calendar.getInstance();
220
int currentHour = now.get(Calendar.HOUR_OF_DAY);
221
222
boolean withinBusinessHours = currentHour >= startHour && currentHour <= endHour;
223
224
if (!withinBusinessHours) {
225
LOGGER.warn("Surrogate authentication denied outside business hours for principal: {}",
226
request.getPrincipalId());
227
}
228
229
return withinBusinessHours;
230
}
231
232
// Getters and setters
233
public int getStartHour() { return startHour; }
234
public void setStartHour(int startHour) { this.startHour = startHour; }
235
public int getEndHour() { return endHour; }
236
public void setEndHour(int endHour) { this.endHour = endHour; }
237
}
238
```
239
240
### JSON Configuration
241
242
Service access strategies can be configured via JSON in the CAS service registry:
243
244
```json
245
{
246
"@class": "org.apereo.cas.services.CasRegisteredService",
247
"serviceId": "^https://example\\.com/.*",
248
"name": "Example Service",
249
"id": 1,
250
"accessStrategy": {
251
"@class": "org.apereo.cas.services.SurrogateRegisteredServiceAccessStrategy",
252
"requireAllAttributes": true,
253
"caseInsensitive": false,
254
"surrogateRequiredAttributes": {
255
"@class": "java.util.HashMap",
256
"role": ["admin", "support"],
257
"department": ["IT"]
258
}
259
}
260
}
261
```
262
263
## Required Dependencies
264
265
These classes depend on types from:
266
267
```java
268
import org.apereo.cas.services.BaseRegisteredServiceAccessStrategy;
269
import org.apereo.cas.services.RegisteredServiceAccessStrategyRequest;
270
import org.apereo.cas.services.util.RegisteredServiceAccessStrategyEvaluator;
271
import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
272
import org.apereo.cas.configuration.support.ExpressionLanguageCapable;
273
import org.apereo.cas.util.LoggingUtils;
274
import org.apereo.cas.util.ResourceUtils;
275
import org.apereo.cas.util.scripting.ExecutableCompiledScriptFactory;
276
import org.apereo.cas.util.spring.SpringExpressionLanguageValueResolver;
277
278
import lombok.EqualsAndHashCode;
279
import lombok.Getter;
280
import lombok.Setter;
281
import lombok.extern.slf4j.Slf4j;
282
import lombok.val;
283
284
import org.springframework.core.io.Resource;
285
286
import java.io.Serial;
287
import java.util.*;
288
```