Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OTEL - Automatic ENDUSER_ID and ENDUSER_ROLE filling #34526

Closed
amoscatelli opened this issue Jul 4, 2023 · 12 comments · Fixed by #34595 or #40466
Closed

OTEL - Automatic ENDUSER_ID and ENDUSER_ROLE filling #34526

amoscatelli opened this issue Jul 4, 2023 · 12 comments · Fixed by #34595 or #40466
Assignees
Labels
area/tracing kind/enhancement New feature or request
Milestone

Comments

@amoscatelli
Copy link
Contributor

amoscatelli commented Jul 4, 2023

Description

Hi guys,
as discussed here #33349 it would be nice for Quarkus OpenTelemetry Extension to automatically fill enduser.id and enduser.role when possible, maybe there could be a configuration to enable this behavior.

I am doing this in my projects with a custom SpanProcessor, but maybe this could be of interest for others too (without reinventing the wheel).

What do you think ?

Implementation ideas

package com.infolabtech.commons;

import jakarta.enterprise.inject.spi.CDI;
import java.security.Principal;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.jwt.JsonWebToken;

public interface Authenticated {

    default Collection<Principal> getPrincipals() {
        return CollectionUtils.select(
                IterableUtils.toList(
                        CDI.current().select(
                                Principal.class
                        )
                ),
                principal -> StringUtils.isNotEmpty(
                        principal.getName()
                )
        );
    }

    default Set<String> getUsernames() {
        return getPrincipals().stream().map(
                Principal::getName
        ).collect(
                Collectors.toSet()
        );
    }

    default Optional<Principal> getPrincipal() {
        return getPrincipals().stream().findFirst();
    }

    default Optional<String> getUsername() {
        return getPrincipal().map(
                Principal::getName
        );
    }

    default Optional<Collection<String>> getGroups() {
        return getPrincipal().filter(
                JsonWebToken.class::isInstance
        ).map(
                JsonWebToken.class::cast
        ).map(
                JsonWebToken::getGroups
        );
    }

}
package com.infolabtech.commons.tracing;

import com.infolabtech.commons.Authenticated;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.microprofile.context.ManagedExecutor;

@ApplicationScoped
public class EndUserSpanProcessor implements SpanProcessor, Authenticated {

    @Inject
    protected ManagedExecutor managedExecutor;

    @Override
    @ActivateRequestContext
    public void onStart(Context parentContext, ReadWriteSpan span) {
        managedExecutor.execute(
                () -> getPrincipal().ifPresent(
                        principal -> span.setAllAttributes(
                                Attributes.of(
                                        SemanticAttributes.ENDUSER_ID,
                                        principal.getName(),
                                        SemanticAttributes.ENDUSER_ROLE,
                                        CollectionUtils.emptyIfNull(
                                                principal.getGroups()
                                        ).toString()
                                )
                        )
                )
        );
    }

    @Override
    public boolean isStartRequired() {
        return Boolean.TRUE;
    }

    @Override
    public void onEnd(ReadableSpan span) {
    }

    @Override
    public boolean isEndRequired() {
        return Boolean.FALSE;
    }

}
@amoscatelli amoscatelli added the kind/enhancement New feature or request label Jul 4, 2023
@amoscatelli
Copy link
Contributor Author

amoscatelli commented Jul 4, 2023

@brunobat maybe we could add a span processor similar to mine only when the configuration is set to true.
Please don't pay too much attention to the Authenticated interface, I pasted that only to show that I get the Principal or the JsonWebToken principal from CDI.

I think the "ManagedExecutor" is ugly, but I found no other way because vert.x complained about being blocked otherwise.
Is there any other way ?

@brunobat
Copy link
Contributor

brunobat commented Jul 4, 2023

Yes, this should be optional and we could integrate with the jwt.

@amoscatelli
Copy link
Contributor Author

Is there any (internal) way to ask quarkus for the current list of roles ?
If we find a way to make this work, independently of the type of Principal, we can deliver this inside the Opentelemetry Extension and make this generic !

@amoscatelli
Copy link
Contributor Author

found it !
io.quarkus.security.identity.SecurityIdentity

That solves everything ...
Can I work on a PR for this ?

@quarkus-bot
Copy link

quarkus-bot bot commented Jul 5, 2023

/cc @radcortez (opentelemetry,tracing)

@geoand
Copy link
Contributor

geoand commented Jul 5, 2023

Can I work on a PR for this ?

I would propose opening opening a draft PR so we can comment on what you've done (we'll need to involve @sberyozkin as well)

@amoscatelli
Copy link
Contributor Author

this what I had in mind ! let me know what do you think (especially about the ManagedExecutor part)

@geoand
Copy link
Contributor

geoand commented Jul 28, 2023

With #34595 in, is this anything left to do on this one @brunobat ?

@brunobat
Copy link
Contributor

No @geoand.
This was done by #34595

@geoand
Copy link
Contributor

geoand commented Jul 28, 2023

👍🏼

@geoand geoand added this to the 3.3 - main milestone Jul 28, 2023
@brunobat
Copy link
Contributor

I'm going to reopen this issue as the current implementation has been removed due to reliability issues (see: #39563) in reporting the user data and also by always requiring the activation of the request context.
Feature removed here: #39648
Backport PR here: #39707
This must still be implemented as discussed here: #39563 (comment)

@brunobat brunobat reopened this Mar 26, 2024
@github-project-automation github-project-automation bot moved this from Done to In Progress in Quarkus Roadmap/Planning Mar 26, 2024
@brunobat brunobat moved this from In Progress to Todo in Quarkus Roadmap/Planning Mar 26, 2024
@brunobat
Copy link
Contributor

Assigning to @michalvavrik.
Please take your time Michal and thanks for working on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment