-
Notifications
You must be signed in to change notification settings - Fork 49
Wire Server Telemetry response header, Fixes AB#3556246 #3062
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
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
c0ff49d
Implement ClientDataInfo model and x-ms-clientdata telemetry pipeline…
Copilot df95ce2
Add unit tests for ClientDataInfo and server telemetry integration po…
Copilot 3eaa693
Remove ENABLE_SERVER_CLIENT_DATA_TELEMETRY flighting - always enable …
Copilot b1ed1f3
Replace fromJson with fromPipeDelimited in token handler; remove from…
Copilot 4c2856c
Merge branch 'dev' into fadi/server-telemetry
fadidurah ecf1f84
fix test
fadidurah 02b46b5
Extract "clientdata" query parameter name to ClientDataInfo.CLIENTDAT…
Copilot 2cb17b4
Add back ENABLE_SERVER_CLIENT_DATA_TELEMETRY flight (default on); add…
Copilot 9aa59a9
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
fadidurah d9bc33f
tweak attributename
fadidurah File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
175 changes: 175 additions & 0 deletions
175
common4j/src/main/com/microsoft/identity/common/java/telemetry/ClientDataInfo.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. | ||
| // | ||
| // This code is licensed under the MIT License. | ||
| // | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| // of this software and associated documentation files(the "Software"), to deal | ||
| // in the Software without restriction, including without limitation the rights | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell | ||
| // copies of the Software, and to permit persons to whom the Software is | ||
| // furnished to do so, subject to the following conditions : | ||
| // | ||
| // The above copyright notice and this permission notice shall be included in | ||
| // all copies or substantial portions of the Software. | ||
| // | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| // THE SOFTWARE. | ||
| package com.microsoft.identity.common.java.telemetry; | ||
|
|
||
| import com.microsoft.identity.common.java.logging.Logger; | ||
| import com.microsoft.identity.common.java.opentelemetry.AttributeName; | ||
| import com.microsoft.identity.common.java.opentelemetry.SpanExtension; | ||
| import com.microsoft.identity.common.java.util.StringUtil; | ||
|
|
||
| import edu.umd.cs.findbugs.annotations.Nullable; | ||
| import io.opentelemetry.api.trace.Span; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
| import lombok.experimental.Accessors; | ||
|
|
||
| /** | ||
| * Model representing server telemetry data from the x-ms-clientdata response header | ||
| * (/token responses) and the clientdata query parameter (/authorize redirect URLs). | ||
| * Both use a pipe-delimited format: account_type|error|sub_error|caller_data_boundary|cloud_instance. | ||
| * Contains server-side error codes, account type, cloud instance, and data boundary info. | ||
| */ | ||
| @Getter | ||
| @Setter | ||
| @Accessors(prefix = "m") | ||
| public class ClientDataInfo { | ||
|
|
||
| private static final String TAG = ClientDataInfo.class.getSimpleName(); | ||
|
|
||
| /** Maximum length for any individual field when emitting to a span. */ | ||
| private static final int MAX_FIELD_LENGTH = 256; | ||
|
|
||
| /** | ||
| * The name of the {@code clientdata} query parameter added to /authorize redirect URIs | ||
| * by eSTS/MSA when {@code clidata=1} is included in the authorization request. | ||
| * Use this constant everywhere the parameter name is referenced to avoid typos. | ||
| */ | ||
| public static final String CLIENTDATA_QUERY_PARAMETER = "clientdata"; | ||
|
|
||
| /** Account type value for MSA accounts. */ | ||
| private static final String ACCOUNT_TYPE_MSA_RAW = "m"; | ||
|
|
||
| /** Account type value for AAD accounts. */ | ||
| private static final String ACCOUNT_TYPE_AAD_RAW = "e"; | ||
|
|
||
| /** Display value for MSA account type. */ | ||
| private static final String ACCOUNT_TYPE_MSA = "MSA"; | ||
|
|
||
| /** Display value for AAD account type. */ | ||
| private static final String ACCOUNT_TYPE_AAD = "AAD"; | ||
|
|
||
| /** | ||
| * Positional index for account_type in the pipe-delimited format: | ||
| * account_type|error|sub_error|caller_data_boundary|cloud_instance | ||
| */ | ||
| private static final int PIPE_INDEX_ACCOUNT_TYPE = 0; | ||
| private static final int PIPE_INDEX_ERROR = 1; | ||
| private static final int PIPE_INDEX_SUB_ERROR = 2; | ||
| private static final int PIPE_INDEX_CALLER_DATA_BOUNDARY = 3; | ||
| private static final int PIPE_INDEX_CLOUD_INSTANCE = 4; | ||
| private static final int PIPE_MIN_SEGMENTS = 3; | ||
|
|
||
| private String mError; | ||
| private String mSubError; | ||
| private String mAccountType; | ||
| private String mCloudInstance; | ||
| private String mCallerDataBoundary; | ||
|
|
||
| /** | ||
| * Parses an already-decoded pipe-delimited clientdata query parameter value. | ||
| * The caller is responsible for URL-decoding before passing (e.g. values from | ||
| * {@link com.microsoft.identity.common.java.util.UrlUtil#getParameters} are | ||
| * already decoded). Decoding twice would corrupt values containing '+' or '%'. | ||
| * Positional format: account_type|error|sub_error|caller_data_boundary|cloud_instance. | ||
| * Requires at least 3 segments. | ||
| * | ||
| * @param decodedValue already-decoded pipe-delimited string, may be null. | ||
| * @return parsed {@link ClientDataInfo}, or null on failure/empty input. | ||
| */ | ||
| @Nullable | ||
| public static ClientDataInfo fromPipeDelimited(@Nullable final String decodedValue) { | ||
| if (StringUtil.isNullOrEmpty(decodedValue)) { | ||
| return null; | ||
| } | ||
| try { | ||
| final String[] segments = decodedValue.split("\\|", -1); | ||
|
|
||
| if (segments.length < PIPE_MIN_SEGMENTS) { | ||
| Logger.warn(TAG, "clientdata pipe-delimited value has fewer than " + PIPE_MIN_SEGMENTS + " segments."); | ||
| return null; | ||
| } | ||
|
|
||
| final ClientDataInfo info = new ClientDataInfo(); | ||
| info.mAccountType = emptyToNull(segments[PIPE_INDEX_ACCOUNT_TYPE]); | ||
| info.mError = emptyToNull(segments[PIPE_INDEX_ERROR]); | ||
| info.mSubError = emptyToNull(segments[PIPE_INDEX_SUB_ERROR]); | ||
| info.mCallerDataBoundary = segments.length > PIPE_INDEX_CALLER_DATA_BOUNDARY | ||
| ? emptyToNull(segments[PIPE_INDEX_CALLER_DATA_BOUNDARY]) : null; | ||
| info.mCloudInstance = segments.length > PIPE_INDEX_CLOUD_INSTANCE | ||
| ? emptyToNull(segments[PIPE_INDEX_CLOUD_INSTANCE]) : null; | ||
| return info; | ||
| } catch (final Exception e) { | ||
| Logger.warn(TAG, "Failed to parse clientdata pipe-delimited value: " + e.getMessage()); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sets each non-null field as a span attribute on the current span via {@link SpanExtension}. | ||
| * account_type values are mapped: "m" -> "MSA", "e" -> "AAD". | ||
| * Each field is truncated to {@value #MAX_FIELD_LENGTH} characters. | ||
| */ | ||
| public void emitToSpan() { | ||
| final Span span = SpanExtension.current(); | ||
| if (mError != null) { | ||
| span.setAttribute(AttributeName.server_error.name(), truncate(mError)); | ||
| } | ||
| if (mSubError != null) { | ||
| span.setAttribute(AttributeName.server_sub_error.name(), truncate(mSubError)); | ||
| } | ||
| if (mAccountType != null) { | ||
| // account_type is an existing AttributeName; reuse it (m -> MSA, e -> AAD). | ||
| final String mappedAccountType = mapAccountType(mAccountType); | ||
| span.setAttribute(AttributeName.account_type.name(), truncate(mappedAccountType)); | ||
| } | ||
| if (mCloudInstance != null) { | ||
| span.setAttribute(AttributeName.server_cloud_instance.name(), truncate(mCloudInstance)); | ||
| } | ||
| if (mCallerDataBoundary != null) { | ||
| span.setAttribute(AttributeName.server_caller_data_boundary.name(), truncate(mCallerDataBoundary)); | ||
| } | ||
| } | ||
|
|
||
| private static String mapAccountType(final String raw) { | ||
| if (ACCOUNT_TYPE_MSA_RAW.equalsIgnoreCase(raw)) { | ||
| return ACCOUNT_TYPE_MSA; | ||
| } else if (ACCOUNT_TYPE_AAD_RAW.equalsIgnoreCase(raw)) { | ||
| return ACCOUNT_TYPE_AAD; | ||
| } | ||
| Logger.warn(TAG, "Unknown account_type value in clientdata; emitting as UNKNOWN."); | ||
| return "UNKNOWN"; | ||
| } | ||
|
|
||
| @Nullable | ||
| private static String truncate(@Nullable final String value) { | ||
| if (value == null || value.length() <= MAX_FIELD_LENGTH) { | ||
| return value; | ||
| } | ||
| return value.substring(0, MAX_FIELD_LENGTH); | ||
| } | ||
|
|
||
| @Nullable | ||
| private static String emptyToNull(final String value) { | ||
| return StringUtil.isNullOrEmpty(value) ? null : value; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.