Skip to content

Commit cea6ecd

Browse files
committed
[SYNCOPE-1916] Metrics (#1195)
1 parent 49e048f commit cea6ecd

File tree

16 files changed

+701
-3
lines changed

16 files changed

+701
-3
lines changed

core/metrics-starter/pom.xml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<parent>
25+
<groupId>org.apache.syncope</groupId>
26+
<artifactId>syncope-core</artifactId>
27+
<version>4.1.0-SNAPSHOT</version>
28+
</parent>
29+
30+
<name>Apache Syncope Core Spring Boot Metrics</name>
31+
<description>Apache Syncope Core Spring Boot Metrics</description>
32+
<groupId>org.apache.syncope.core</groupId>
33+
<artifactId>syncope-core-metrics-starter</artifactId>
34+
<packaging>jar</packaging>
35+
36+
<properties>
37+
<rootpom.basedir>${basedir}/../..</rootpom.basedir>
38+
</properties>
39+
40+
<dependencies>
41+
<dependency>
42+
<groupId>org.apache.syncope.core</groupId>
43+
<artifactId>syncope-core-starter</artifactId>
44+
<version>${project.version}</version>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>io.micrometer</groupId>
49+
<artifactId>micrometer-core</artifactId>
50+
</dependency>
51+
</dependencies>
52+
53+
<build>
54+
<plugins>
55+
<plugin>
56+
<groupId>org.apache.maven.plugins</groupId>
57+
<artifactId>maven-checkstyle-plugin</artifactId>
58+
</plugin>
59+
</plugins>
60+
61+
<resources>
62+
<resource>
63+
<directory>src/main/resources</directory>
64+
<filtering>true</filtering>
65+
</resource>
66+
</resources>
67+
</build>
68+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.syncope.core.provisioning.java.propagation;
20+
21+
import io.micrometer.core.instrument.Counter;
22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import java.util.Collection;
24+
import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
25+
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
26+
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
27+
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
28+
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
29+
import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
30+
import org.apache.syncope.core.provisioning.api.AuditManager;
31+
import org.apache.syncope.core.provisioning.api.ConnectorManager;
32+
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
33+
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
34+
import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
35+
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
36+
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
37+
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
38+
import org.springframework.context.ApplicationEventPublisher;
39+
import org.springframework.core.task.AsyncTaskExecutor;
40+
41+
public class InstrumentedPriorityPropagationTaskExecutor extends PriorityPropagationTaskExecutor {
42+
43+
protected final MeterRegistry meterRegistry;
44+
45+
public InstrumentedPriorityPropagationTaskExecutor(
46+
final ConnectorManager connectorManager,
47+
final ConnObjectUtils connObjectUtils,
48+
final TaskDAO taskDAO,
49+
final ExternalResourceDAO resourceDAO,
50+
final PlainSchemaDAO plainSchemaDAO,
51+
final NotificationManager notificationManager,
52+
final AuditManager auditManager,
53+
final TaskDataBinder taskDataBinder,
54+
final AnyUtilsFactory anyUtilsFactory,
55+
final TaskUtilsFactory taskUtilsFactory,
56+
final OutboundMatcher outboundMatcher,
57+
final PlainAttrValidationManager validator,
58+
final ApplicationEventPublisher publisher,
59+
final AsyncTaskExecutor taskExecutor,
60+
final MeterRegistry meterRegistry) {
61+
62+
super(connectorManager, connObjectUtils, taskDAO, resourceDAO, plainSchemaDAO, notificationManager,
63+
auditManager, taskDataBinder, anyUtilsFactory, taskUtilsFactory, outboundMatcher, validator, publisher,
64+
taskExecutor);
65+
this.meterRegistry = meterRegistry;
66+
}
67+
68+
@Override
69+
public PropagationReporter execute(
70+
final Collection<PropagationTaskInfo> taskInfos,
71+
final boolean nullPriorityAsync,
72+
final String executor) {
73+
74+
PropagationReporter reporter = super.execute(taskInfos, nullPriorityAsync, executor);
75+
76+
reporter.getStatuses().forEach(status -> Counter.builder(
77+
"syncope.propagation." + status.getStatus().name().toLowerCase() + ".count").
78+
description("The total number of propagations with status " + status.getStatus().name()).
79+
tag("resource", status.getResource()).
80+
register(meterRegistry).
81+
increment());
82+
83+
return reporter;
84+
}
85+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.syncope.core.spring.security;
20+
21+
import io.micrometer.core.instrument.Counter;
22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import java.util.List;
24+
import java.util.Optional;
25+
import java.util.Set;
26+
import java.util.function.Function;
27+
import org.apache.commons.lang3.tuple.Pair;
28+
import org.apache.commons.lang3.tuple.Triple;
29+
import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
30+
import org.apache.syncope.core.persistence.api.EncryptorManager;
31+
import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
32+
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
33+
import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
34+
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
35+
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
36+
import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
37+
import org.apache.syncope.core.persistence.api.dao.RoleDAO;
38+
import org.apache.syncope.core.persistence.api.dao.UserDAO;
39+
import org.apache.syncope.core.persistence.api.entity.user.User;
40+
import org.apache.syncope.core.provisioning.api.AuditManager;
41+
import org.apache.syncope.core.provisioning.api.ConnectorManager;
42+
import org.apache.syncope.core.provisioning.api.MappingManager;
43+
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
44+
import org.springframework.security.authentication.DisabledException;
45+
import org.springframework.security.core.Authentication;
46+
47+
public class InstrumentedAuthDataAccessor extends AuthDataAccessor {
48+
49+
protected static final String SUCCESS_TYPE = ".success";
50+
51+
protected static final String FAILURE_TYPE = ".failure";
52+
53+
protected static final String MUST_CHANGE_PASSWORD_TYPE = ".failure";
54+
55+
protected static final String NOT_FOUND_TYPE = ".notfound";
56+
57+
protected static final String DISABLED_TYPE = ".disabled";
58+
59+
protected static final Function<String, String> USERNAME = suffix -> "syncope.auth.username" + suffix + ".count";
60+
61+
protected static final Function<String, String> JWT = suffix -> "syncope.auth.jwt" + suffix + ".count";
62+
63+
protected static final Function<String, String> SUCCESS_DESC =
64+
type -> "The total number of succeeded " + type + " logins";
65+
66+
protected static final Function<String, String> FAILURE_DESC =
67+
type -> "The total number of failed " + type + " logins";
68+
69+
protected static final Function<String, String> MUST_CHANGE_PASSWORD_DESC =
70+
type -> "The total number of mustChangePassword users attempting to perform " + type + " login";
71+
72+
protected static final Function<String, String> NOT_FOUND_DESC =
73+
type -> "The total number of not found users attempting to perform " + type + " login";
74+
75+
protected static final Function<String, String> DISABLED_DESC =
76+
type -> "The total number of disabled users attempting to perform " + type + " login";
77+
78+
protected final MeterRegistry meterRegistry;
79+
80+
public InstrumentedAuthDataAccessor(
81+
final SecurityProperties securityProperties,
82+
final EncryptorManager encryptorManager,
83+
final RealmSearchDAO realmSearchDAO,
84+
final UserDAO userDAO,
85+
final GroupDAO groupDAO,
86+
final AnySearchDAO anySearchDAO,
87+
final AccessTokenDAO accessTokenDAO,
88+
final ConfParamOps confParamOps,
89+
final RoleDAO roleDAO,
90+
final DelegationDAO delegationDAO,
91+
final ExternalResourceDAO resourceDAO,
92+
final ConnectorManager connectorManager,
93+
final AuditManager auditManager,
94+
final MappingManager mappingManager,
95+
final List<JWTSSOProvider> jwtSSOProviders,
96+
final MeterRegistry meterRegistry) {
97+
98+
super(securityProperties, encryptorManager, realmSearchDAO, userDAO, groupDAO, anySearchDAO, accessTokenDAO,
99+
confParamOps, roleDAO, delegationDAO, resourceDAO, connectorManager, auditManager, mappingManager,
100+
jwtSSOProviders);
101+
this.meterRegistry = meterRegistry;
102+
}
103+
104+
@Override
105+
public Triple<User, Boolean, String> authenticate(final String domain, final Authentication authentication) {
106+
try {
107+
Triple<User, Boolean, String> auth = super.authenticate(domain, authentication);
108+
109+
Optional.ofNullable(auth.getLeft()).ifPresentOrElse(
110+
user -> {
111+
Counter.builder(auth.getMiddle() ? USERNAME.apply(SUCCESS_TYPE) : USERNAME.apply(FAILURE_TYPE)).
112+
description(auth.getMiddle()
113+
? SUCCESS_DESC.apply("username") : FAILURE_DESC.apply("username")).
114+
tag("realm", user.getRealm().getFullPath()).
115+
register(meterRegistry).
116+
increment();
117+
if (user.isMustChangePassword()) {
118+
Counter.builder(USERNAME.apply(MUST_CHANGE_PASSWORD_TYPE)).
119+
description(MUST_CHANGE_PASSWORD_DESC.apply("username")).
120+
register(meterRegistry).
121+
increment();
122+
}
123+
},
124+
() -> Counter.builder(USERNAME.apply(NOT_FOUND_TYPE)).
125+
description(NOT_FOUND_DESC.apply("username")).
126+
register(meterRegistry).
127+
increment());
128+
129+
return auth;
130+
} catch (DisabledException e) {
131+
Counter.builder(USERNAME.apply(DISABLED_TYPE)).
132+
description(DISABLED_DESC.apply("JWT")).
133+
register(meterRegistry).
134+
increment();
135+
throw e;
136+
}
137+
}
138+
139+
@Override
140+
public Pair<String, Set<SyncopeGrantedAuthority>> authenticate(final JWTAuthentication authentication) {
141+
try {
142+
Pair<String, Set<SyncopeGrantedAuthority>> auth = super.authenticate(authentication);
143+
144+
if (MUST_CHANGE_PASSWORD_AUTHORITIES.equals(auth.getRight())) {
145+
Counter.builder(JWT.apply(MUST_CHANGE_PASSWORD_TYPE)).
146+
description(MUST_CHANGE_PASSWORD_DESC.apply("JWT")).
147+
register(meterRegistry).
148+
increment();
149+
} else {
150+
Counter.builder(JWT.apply(SUCCESS_TYPE)).
151+
description(SUCCESS_DESC.apply("JWT")).
152+
register(meterRegistry).
153+
increment();
154+
}
155+
156+
return auth;
157+
} catch (AuthenticationCredentialsNotFoundException e) {
158+
Counter.builder(JWT.apply(NOT_FOUND_TYPE)).
159+
description(NOT_FOUND_DESC.apply("JWT")).
160+
register(meterRegistry).
161+
increment();
162+
throw e;
163+
} catch (DisabledException e) {
164+
Counter.builder(JWT.apply(DISABLED_TYPE)).
165+
description(DISABLED_DESC.apply("JWT")).
166+
register(meterRegistry).
167+
increment();
168+
throw e;
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)