Skip to content

Commit 30a61b2

Browse files
committed
Observe Events
1 parent b965320 commit 30a61b2

15 files changed

+571
-6
lines changed

Diff for: core/src/main/java/org/springframework/security/access/event/AbstractAuthorizationEvent.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
package org.springframework.security.access.event;
1818

19-
import org.springframework.context.ApplicationEvent;
19+
import org.springframework.security.event.SecurityEvent;
2020

2121
/**
2222
* Abstract superclass for all security interception related events.
2323
*
2424
* @author Ben Alex
2525
*/
26-
public abstract class AbstractAuthorizationEvent extends ApplicationEvent {
26+
public abstract class AbstractAuthorizationEvent extends SecurityEvent {
2727

2828
/**
2929
* Construct the event, passing in the secure object being intercepted.

Diff for: core/src/main/java/org/springframework/security/authentication/event/AbstractAuthenticationEvent.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package org.springframework.security.authentication.event;
1818

19-
import org.springframework.context.ApplicationEvent;
2019
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.event.SecurityEvent;
2121

2222
/**
2323
* Represents an application authentication event.
@@ -28,7 +28,7 @@
2828
*
2929
* @author Ben Alex
3030
*/
31-
public abstract class AbstractAuthenticationEvent extends ApplicationEvent {
31+
public abstract class AbstractAuthenticationEvent extends SecurityEvent {
3232

3333
public AbstractAuthenticationEvent(Authentication authentication) {
3434
super(authentication);

Diff for: core/src/main/java/org/springframework/security/authentication/event/AbstractAuthenticationFailureEvent.java

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public AbstractAuthenticationFailureEvent(Authentication authentication, Authent
3535
this.exception = exception;
3636
}
3737

38+
public Authentication getAuthentication() {
39+
return (Authentication) getSource();
40+
}
41+
3842
public AuthenticationException getException() {
3943
return this.exception;
4044
}

Diff for: core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.context.ApplicationEvent;
2222
import org.springframework.security.authorization.AuthorizationDecision;
2323
import org.springframework.security.core.Authentication;
24+
import org.springframework.security.event.SecurityEvent;
2425

2526
/**
2627
* An {@link ApplicationEvent} which indicates failed authorization.
@@ -29,7 +30,7 @@
2930
* @author Josh Cummings
3031
* @since 5.7
3132
*/
32-
public class AuthorizationDeniedEvent<T> extends ApplicationEvent {
33+
public class AuthorizationDeniedEvent<T> extends SecurityEvent {
3334

3435
private final Supplier<Authentication> authentication;
3536

Diff for: core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.context.ApplicationEvent;
2222
import org.springframework.security.authorization.AuthorizationDecision;
2323
import org.springframework.security.core.Authentication;
24+
import org.springframework.security.event.SecurityEvent;
2425
import org.springframework.util.Assert;
2526

2627
/**
@@ -30,7 +31,7 @@
3031
* @author Josh Cummings
3132
* @since 5.7
3233
*/
33-
public class AuthorizationGrantedEvent<T> extends ApplicationEvent {
34+
public class AuthorizationGrantedEvent<T> extends SecurityEvent {
3435

3536
private final Supplier<Authentication> authentication;
3637

Diff for: core/src/main/java/org/springframework/security/core/Authentication.java

+4
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,8 @@ public interface Authentication extends Principal, Serializable {
134134
*/
135135
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
136136

137+
default String getAuthenticationType() {
138+
return this.getClass().getSimpleName();
139+
}
140+
137141
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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 org.springframework.security.event;
18+
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.observation.ObservationConvention;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.security.authentication.AuthenticationObservationContext;
24+
import org.springframework.security.authentication.AuthenticationObservationConvention;
25+
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
26+
27+
/**
28+
* A strategy to collect {@link KeyValues} for
29+
* {@link org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent}s
30+
*
31+
* @author Josh Cummings
32+
* @since 6.0
33+
*/
34+
public final class AuthenticationFailureEventKeyValuesConverter
35+
implements Converter<AbstractAuthenticationFailureEvent, KeyValues> {
36+
37+
private final ObservationConvention<AuthenticationObservationContext> convention = new AuthenticationObservationConvention();
38+
39+
@Override
40+
public KeyValues convert(AbstractAuthenticationFailureEvent event) {
41+
return this.convention.getLowCardinalityKeyValues(AuthenticationObservationContext.fromEvent(event));
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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 org.springframework.security.event;
18+
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.observation.ObservationConvention;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.security.authentication.AuthenticationObservationContext;
24+
import org.springframework.security.authentication.AuthenticationObservationConvention;
25+
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
26+
27+
/**
28+
* A strategy to collect {@link KeyValues} for
29+
* {@link org.springframework.security.authentication.event.AuthenticationSuccessEvent}s
30+
*
31+
* @author Josh Cummings
32+
* @since 6.0
33+
*/
34+
public final class AuthenticationSuccessEventKeyValuesConverter
35+
implements Converter<AuthenticationSuccessEvent, KeyValues> {
36+
37+
private final ObservationConvention<AuthenticationObservationContext> convention = new AuthenticationObservationConvention();
38+
39+
@Override
40+
public KeyValues convert(AuthenticationSuccessEvent event) {
41+
return this.convention.getLowCardinalityKeyValues(AuthenticationObservationContext.fromEvent(event));
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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 org.springframework.security.event;
18+
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.observation.ObservationConvention;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.security.authorization.AuthorizationObservationContext;
24+
import org.springframework.security.authorization.AuthorizationObservationConvention;
25+
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
26+
27+
/**
28+
* A strategy to collect {@link KeyValues} for
29+
* {@link org.springframework.security.authorization.event.AuthorizationDeniedEvent}s
30+
*
31+
* @author Josh Cummings
32+
* @since 6.0
33+
*/
34+
@SuppressWarnings("rawtypes")
35+
public final class AuthorizationDeniedEventKeyValuesConverter
36+
implements Converter<AuthorizationDeniedEvent, KeyValues> {
37+
38+
private final ObservationConvention<AuthorizationObservationContext<?>> convention = new AuthorizationObservationConvention();
39+
40+
@Override
41+
public KeyValues convert(AuthorizationDeniedEvent event) {
42+
return this.convention.getLowCardinalityKeyValues(AuthorizationObservationContext.fromEvent(event));
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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 org.springframework.security.event;
18+
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.observation.ObservationConvention;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.security.authorization.AuthorizationObservationContext;
24+
import org.springframework.security.authorization.AuthorizationObservationConvention;
25+
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
26+
27+
/**
28+
* A strategy to collect {@link KeyValues} for
29+
* {@link org.springframework.security.authorization.event.AuthorizationGrantedEvent}s
30+
*
31+
* @author Josh Cummings
32+
* @since 6.0
33+
*/
34+
@SuppressWarnings("rawtypes")
35+
public final class AuthorizationGrantedEventKeyValuesConverter
36+
implements Converter<AuthorizationGrantedEvent, KeyValues> {
37+
38+
private final ObservationConvention<AuthorizationObservationContext<?>> convention = new AuthorizationObservationConvention();
39+
40+
@Override
41+
public KeyValues convert(AuthorizationGrantedEvent event) {
42+
return this.convention.getLowCardinalityKeyValues(AuthorizationObservationContext.fromEvent(event));
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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 org.springframework.security.event;
18+
19+
import java.util.Collection;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
23+
import io.micrometer.common.KeyValues;
24+
import io.micrometer.observation.ObservationRegistry;
25+
26+
import org.springframework.context.ApplicationEvent;
27+
import org.springframework.context.ApplicationListener;
28+
import org.springframework.context.event.SmartApplicationListener;
29+
import org.springframework.core.convert.converter.Converter;
30+
import org.springframework.util.Assert;
31+
32+
/**
33+
* A {@link ApplicationListener} that publishes {@link SecurityEvent}s to an
34+
* {@link io.micrometer.observation.ObservationRegistry}
35+
*
36+
* @author Josh Cummings
37+
* @since 6.0
38+
*/
39+
public final class DelegatingObservationSecurityEventListener implements ApplicationListener<SecurityEvent> {
40+
41+
private final Collection<SmartApplicationListener> listeners;
42+
43+
private DelegatingObservationSecurityEventListener(Collection<SmartApplicationListener> listeners) {
44+
this.listeners = listeners;
45+
}
46+
47+
public static Builder withDefaults(ObservationRegistry registry) {
48+
Assert.notNull(registry, "registry cannot be null");
49+
return new Builder(registry);
50+
}
51+
52+
@Override
53+
public void onApplicationEvent(SecurityEvent event) {
54+
for (SmartApplicationListener listener : this.listeners) {
55+
Object source = event.getSource();
56+
if (source != null && listener.supportsEventType(event.getClass())
57+
&& listener.supportsSourceType(source.getClass())) {
58+
listener.onApplicationEvent(event);
59+
return;
60+
}
61+
}
62+
}
63+
64+
public static final class Builder {
65+
66+
private final ObservationRegistry registry;
67+
68+
private final Map<String, SmartApplicationListener> listeners;
69+
70+
private Builder(ObservationRegistry registry) {
71+
this.registry = registry;
72+
this.listeners = new LinkedHashMap<>();
73+
}
74+
75+
public <T extends SecurityEvent> Builder add(Class<T> clazz) {
76+
return add(clazz, (event) -> KeyValues.empty());
77+
}
78+
79+
public <T extends SecurityEvent> Builder add(Class<T> clazz, Converter<T, KeyValues> keyValuesConverter) {
80+
return add(clazz, new ObservationSecurityEventListener<>(this.registry, keyValuesConverter));
81+
}
82+
83+
public <T extends SecurityEvent> Builder add(Class<T> clazz, ObservationSecurityEventListener<T> listener) {
84+
this.listeners.put(clazz.getName(), new SecuritySmartApplicationListener<>(clazz, listener));
85+
return this;
86+
}
87+
88+
public DelegatingObservationSecurityEventListener build() {
89+
add(SecurityEvent.class);
90+
return new DelegatingObservationSecurityEventListener(this.listeners.values());
91+
}
92+
93+
private static class SecuritySmartApplicationListener<T extends SecurityEvent>
94+
implements SmartApplicationListener {
95+
96+
private final Class<T> eventType;
97+
98+
private final ApplicationListener<T> listener;
99+
100+
SecuritySmartApplicationListener(Class<T> eventType, ApplicationListener<T> listener) {
101+
this.eventType = eventType;
102+
this.listener = listener;
103+
}
104+
105+
@Override
106+
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
107+
return this.eventType.isAssignableFrom(eventType);
108+
}
109+
110+
@Override
111+
public void onApplicationEvent(ApplicationEvent event) {
112+
this.listener.onApplicationEvent((T) event);
113+
}
114+
115+
}
116+
117+
}
118+
119+
}

0 commit comments

Comments
 (0)