Skip to content

Commit 741c5e0

Browse files
committed
redesign security
Signed-off-by: tvallin <[email protected]>
1 parent 62d2028 commit 741c5e0

File tree

9 files changed

+115
-354
lines changed

9 files changed

+115
-354
lines changed

openapi/src/main/java/io/helidon/openapi/OpenApiFeature.java

+45-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.file.Paths;
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
29+
import java.util.LinkedList;
2930
import java.util.List;
3031
import java.util.Objects;
3132
import java.util.Optional;
@@ -45,14 +46,15 @@
4546
import io.helidon.http.WritableHeaders;
4647
import io.helidon.webserver.http.HttpRules;
4748
import io.helidon.webserver.http.HttpService;
49+
import io.helidon.webserver.http.SecureHandler;
4850
import io.helidon.webserver.http.ServerRequest;
4951
import io.helidon.webserver.http.ServerResponse;
50-
import io.helidon.webserver.servicecommon.HelidonSecureFeatureSupport;
52+
import io.helidon.webserver.servicecommon.HelidonFeatureSupport;
5153

5254
/**
5355
* Behavior shared between the SE and MP OpenAPI feature implementations.
5456
*/
55-
public abstract class OpenApiFeature extends HelidonSecureFeatureSupport {
57+
public abstract class OpenApiFeature extends HelidonFeatureSupport {
5658

5759
/**
5860
* Default media type used in responses in absence of incoming Accept
@@ -81,6 +83,8 @@ public abstract class OpenApiFeature extends HelidonSecureFeatureSupport {
8183
private final MediaType[] preferredMediaTypeOrdering;
8284
private final MediaType[] mediaTypesSupportedByUi;
8385
private final ConcurrentMap<OpenAPIMediaType, String> cachedDocuments = new ConcurrentHashMap<>();
86+
private final boolean permitAll;
87+
private final String[] roles;
8488
/**
8589
* Constructor for the feature.
8690
*
@@ -93,6 +97,8 @@ protected OpenApiFeature(System.Logger logger, Builder<?, ?> builder) {
9397
ui = prepareUi(builder);
9498
mediaTypesSupportedByUi = ui.supportedMediaTypes();
9599
preferredMediaTypeOrdering = preparePreferredMediaTypeOrdering(mediaTypesSupportedByUi);
100+
permitAll = builder.permitAll;
101+
roles = builder.roles.toArray(new String[0]);
96102
}
97103

98104
/**
@@ -151,11 +157,6 @@ private static MediaType[] preparePreferredMediaTypeOrdering(MediaType[] uiTypes
151157
return result;
152158
}
153159

154-
private void configureRoutes(HttpRules rules) {
155-
authorizeRules(rules);
156-
rules.get("/", this::prepareResponse);
157-
}
158-
159160
private static ClassLoader getContextClassLoader() {
160161
return Thread.currentThread().getContextClassLoader();
161162
}
@@ -172,6 +173,9 @@ private OpenApiUi prepareUi(Builder<?, ?> builder) {
172173
}
173174

174175
private void configureRoutes(HttpRules rules) {
176+
if (!permitAll) {
177+
rules.any(SecureHandler.authorize(roles));
178+
}
175179
rules.get("/", this::prepareResponse);
176180
}
177181

@@ -418,7 +422,11 @@ MediaType mediaType() {
418422
* @param <T> specific concrete type of {@link OpenApiFeature} the builder creates
419423
*/
420424
public abstract static class Builder<B extends Builder<B, T>, T extends OpenApiFeature>
421-
extends HelidonSecureFeatureSupport.Builder<B, T> {
425+
extends HelidonFeatureSupport.Builder<B, T> {
426+
427+
private boolean permitAll = false;
428+
429+
private final List<String> roles = new LinkedList<>();
422430

423431
/**
424432
* Config key for the OpenAPI section.
@@ -449,6 +457,12 @@ public B config(Config config) {
449457
.ifPresent(this::staticFile);
450458
config.get(OpenApiUi.Builder.OPENAPI_UI_CONFIG_KEY)
451459
.ifExists(uiBuilder::config);
460+
config.get("permit-all")
461+
.asBoolean()
462+
.ifPresent(this::permitAll);
463+
config.get("roles")
464+
.asList(String.class)
465+
.ifPresent(roles::addAll);
452466
return identity();
453467
}
454468

@@ -496,6 +510,29 @@ public OpenApiStaticFile staticFile() {
496510
: staticFile;
497511
}
498512

513+
/**
514+
* If {@code permitAll} is {@code true} and security enabled, the feature
515+
* endpoints will be authorized. Default value is {@code false}.
516+
*
517+
* @param permitAll permit all metrics endpoint
518+
* @return updated builder instance
519+
*/
520+
public B permitAll(boolean permitAll) {
521+
this.permitAll = permitAll;
522+
return identity();
523+
}
524+
525+
/**
526+
* Set hint for role names the user is expected to be in.
527+
*
528+
* @param role role name
529+
* @return updated builder instance
530+
*/
531+
public B addRoleHint(String role) {
532+
roles.add(role);
533+
return identity();
534+
}
535+
499536
/**
500537
* Returns the logger for the OpenAPI feature instance.
501538
*

tests/integration/security/gh2297/src/main/resources/application.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ security:
3434
roles-allowed:
3535
user:
3636
- admin
37+
metrics:
38+
permit-all: true

webserver/observe/metrics/src/main/java/io/helidon/webserver/observe/metrics/MetricsFeature.java

+5-95
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@
3939
import io.helidon.webserver.http.HttpRouting;
4040
import io.helidon.webserver.http.HttpRules;
4141
import io.helidon.webserver.http.HttpService;
42+
import io.helidon.webserver.http.SecureHandler;
4243
import io.helidon.webserver.http.ServerRequest;
4344
import io.helidon.webserver.http.ServerResponse;
44-
import io.helidon.webserver.servicecommon.HelidonSecureFeatureSupport;
4545

4646
import static io.helidon.http.HeaderNames.ALLOW;
4747
import static io.helidon.http.Status.METHOD_NOT_ALLOWED_405;
4848
import static io.helidon.http.Status.NOT_FOUND_404;
4949
import static io.helidon.http.Status.OK_200;
5050

51-
public class MetricsFeature extends HelidonSecureFeatureSupport {
51+
class MetricsFeature {
5252

5353
/**
5454
* Prefix for key performance indicator metrics names.
@@ -195,7 +195,9 @@ private void getOrOptionsMatching(MediaType mediaType,
195195
}
196196

197197
private void setUpEndpoints(HttpRules rules) {
198-
authorizeRules(rules);
198+
if (!metricsConfig.permitAll()) {
199+
rules.any(SecureHandler.authorize(metricsConfig.roles().toArray(new String[0])));
200+
}
199201
// routing to root of metrics
200202
// As of Helidon 4, this is the only path we should need because scope-based or metric-name-based
201203
// selection should use query parameters instead of paths.
@@ -269,98 +271,6 @@ private void setUpDisabledEndpoints(HttpRules rules) {
269271
.options("/", DISABLED_ENDPOINT_HANDLER);
270272
}
271273

272-
/**
273-
* Separate metrics service class with an afterStop method that is properly invoked.
274-
*/
275-
@Configured(root = true, prefix = "metrics")
276-
public static class Builder extends HelidonSecureFeatureSupport.Builder<Builder, MetricsFeature> {
277-
private LazyValue<MeterRegistry> meterRegistry;
278-
private MetricsConfig.Builder metricsSettingsBuilder = MetricsConfig.builder();
279-
280-
private Builder() {
281-
super("metrics");
282-
}
283-
284-
/**
285-
* Creates a new builder.
286-
*
287-
* @param serviceName service name to register the feature with
288-
*/
289-
protected Builder(String serviceName) {
290-
super(serviceName);
291-
}
292-
293-
@Override
294-
public void routing(HttpRules rules) {
295-
if (metricsConfig.enabled()) {
296-
setUpEndpoints(rules);
297-
} else {
298-
setUpDisabledEndpoints(rules);
299-
}
300-
return new MetricsFeature(this);
301-
}
302-
303-
/**
304-
* Override default configuration.
305-
*
306-
* @param config configuration instance
307-
* @return updated builder instance
308-
* @see io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig.Builder Details about key
309-
* performance metrics configuration
310-
*/
311-
public Builder config(Config config) {
312-
super.config(config);
313-
metricsSettingsBuilder.config(config);
314-
return this;
315-
}
316-
317-
/**
318-
* Assigns {@code MetricsSettings} which will be used in creating the {@code MetricsSupport} instance at build-time.
319-
*
320-
* @param metricsSettingsBuilder the metrics settings to assign for use in building the {@code MetricsSupport} instance
321-
* @return updated builder
322-
*/
323-
@ConfiguredOption(mergeWithParent = true,
324-
type = MetricsConfig.class)
325-
public Builder metricsConfig(MetricsConfig.Builder metricsSettingsBuilder) {
326-
this.metricsSettingsBuilder = metricsSettingsBuilder;
327-
return this;
328-
}
329-
330-
/**
331-
* If you want to have multiple meter registries with different
332-
* endpoints, you may create them using
333-
* {@snippet :
334-
* MeterRegistry meterRegistry = MetricsFactory.getInstance()
335-
* .createMeterRegistry(metricsConfig);
336-
* MetricsFeature.builder()
337-
* .meterRegistry(meterRegistry) // further settings on the feature builder, etc.
338-
* }
339-
* where {@code metricsConfig} in each case has different
340-
* {@link #webContext(String)} settings}.
341-
* <p>
342-
* If this method is not called,
343-
* {@link MetricsFeature} would use the shared
344-
* instance as provided by
345-
* {@link io.helidon.metrics.api.MetricsFactory#globalRegistry()}.
346-
*
347-
* @param meterRegistry meterRegistry to use in this metric support
348-
* @return updated builder instance
349-
*/
350-
public Builder meterRegistry(MeterRegistry meterRegistry) {
351-
this.meterRegistry = LazyValue.create(() -> meterRegistry);
352-
return this;
353-
}
354-
355-
MeterRegistry meterRegistry() {
356-
return meterRegistry.get();
357-
}
358-
359-
MetricsConfig metricsConfig() {
360-
return metricsSettingsBuilder.build();
361-
}
362-
}
363-
364274
/**
365275
* Separate metrics service class with an afterStop method that is properly invoked.
366276
*/

webserver/service-common/src/main/java/io/helidon/webserver/servicecommon/HelidonSecureFeatureSupport.java

-94
This file was deleted.

0 commit comments

Comments
 (0)