-
Notifications
You must be signed in to change notification settings - Fork 588
Add IdentityContextUtil methods to resolve client IP and UA from Request #7632
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,28 +32,37 @@ | |||||||||||||||||||||
| public class Request { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private final List<Header> headers = new ArrayList<>(); | ||||||||||||||||||||||
| private final String ipAddress; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private Request(Builder builder) { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| this.headers.addAll(builder.headers); | ||||||||||||||||||||||
| this.ipAddress = builder.ipAddress; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public List<Header> getHeaders() { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return Collections.unmodifiableList(headers); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public String getIpAddress() { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return ipAddress; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Builder for the Request. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| public static class Builder { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private final List<Header> headers = new ArrayList<>(); | ||||||||||||||||||||||
| private String ipAddress; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public Builder fromHttpRequest(HttpServletRequest request) { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (request != null) { | ||||||||||||||||||||||
| resolveHeaders(request); | ||||||||||||||||||||||
| this.ipAddress = request.getRemoteAddr(); | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify null handling in IdentityContextUtil. The Run the following script to verify how IdentityContextUtil handles null IP addresses: 🏁 Script executed: #!/bin/bash
# Description: Verify that IdentityContextUtil handles null ipAddress properly
# Search for getClientIpAddress implementations and check null handling
ast-grep --pattern $'getClientIpAddress($$$) {
$$$
}'
# Also check for direct usage of Request.getIpAddress()
rg -nP -A 5 'getIpAddress\(\)' --type=javaLength of output: 1824 🏁 Script executed: #!/bin/bash
# Get full context around IdentityContextUtil line 79 and find getClientIpAddress method
echo "=== Context around line 79 in IdentityContextUtil ==="
rg -nP -B 10 -A 10 'request\.getIpAddress\(\)' \
components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.java
echo -e "\n=== Looking for getClientIpAddress method ==="
rg -nP 'getClientIpAddress|ClientIpAddress' \
components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.java
echo -e "\n=== Full IdentityContextUtil file to understand structure ==="
cat components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.javaLength of output: 6846 Handle potential null return from IP address fallback path. Verification confirms that
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
62
to
66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 1
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return this; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| /* | ||
| * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). | ||
| * | ||
| * WSO2 LLC. licenses this file to you under the Apache License, | ||
| * Version 2.0 (the "License"); you may not use this file except | ||
| * in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
|
|
||
| package org.wso2.carbon.identity.core.context.util; | ||
|
|
||
| import org.apache.commons.collections.CollectionUtils; | ||
| import org.apache.commons.lang.StringUtils; | ||
| import org.apache.commons.logging.Log; | ||
| import org.apache.commons.logging.LogFactory; | ||
| import org.wso2.carbon.identity.base.IdentityConstants; | ||
| import org.wso2.carbon.identity.core.context.IdentityContext; | ||
| import org.wso2.carbon.identity.core.context.model.Header; | ||
| import org.wso2.carbon.identity.core.context.model.Request; | ||
| import org.wso2.carbon.identity.core.util.IdentityUtil; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Utility class for IdentityContext related operations. | ||
| */ | ||
| public class IdentityContextUtil { | ||
|
Check warning on line 36 in components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.java
|
||
|
|
||
| private static final Log LOG = LogFactory.getLog(IdentityContextUtil.class); | ||
|
|
||
| /** | ||
| * Resolves the client IP address from the given IdentityContext. | ||
| * | ||
| * @return The client IP address if found, else falls back to the request IP address. | ||
| */ | ||
| public static String getClientIpAddress() { | ||
|
Check failure on line 45 in components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.java
|
||
|
|
||
| Request request = IdentityContext.getThreadLocalIdentityContext().getRequest(); | ||
| if (request == null) { | ||
| LOG.debug("IdentityContext or Request object is null. Cannot resolve client IP address."); | ||
| return null; | ||
| } | ||
|
|
||
| List<Header> headerList = request.getHeaders(); | ||
| if (CollectionUtils.isEmpty(headerList)) { | ||
| LOG.debug("Request headers are empty. Falling back to request IP address."); | ||
| return request.getIpAddress(); | ||
| } | ||
|
|
||
| for (String ipHeader : IdentityConstants.HEADERS_WITH_IP) { | ||
| for (Header header : headerList) { | ||
| if (ipHeader.equalsIgnoreCase(header.getName())) { | ||
| List<String> values = header.getValue(); | ||
| if (CollectionUtils.isNotEmpty(values)) { | ||
| if (LOG.isDebugEnabled() && values.size() > 1) { | ||
| LOG.debug("Multiple values found for header: " + ipHeader + | ||
|
Check failure on line 65 in components/identity-core/org.wso2.carbon.identity.core/src/main/java/org/wso2/carbon/identity/core/context/util/IdentityContextUtil.java
|
||
| ". Using the first value as the client IP address."); | ||
| } | ||
| String ip = values.get(0); | ||
| if (StringUtils.isNotEmpty(ip) && !IdentityConstants.UNKNOWN.equalsIgnoreCase(ip)) { | ||
| return IdentityUtil.getFirstIP(ip); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| LOG.debug("Cannot resolve client IP address from the request headers. Falling back to request IP address."); | ||
| return request.getIpAddress(); | ||
| } | ||
|
|
||
| /** | ||
| * Resolves the client User-Agent from the given Request. | ||
| * | ||
| * @return The client User-Agent if found, else null. | ||
| */ | ||
| public static String getClientUserAgent() { | ||
|
|
||
| Request request = IdentityContext.getThreadLocalIdentityContext().getRequest(); | ||
| if (request == null) { | ||
| LOG.debug("IdentityContext or Request object is null. Cannot resolve User-Agent."); | ||
| return null; | ||
| } | ||
|
|
||
| List<Header> headerList = request.getHeaders(); | ||
| if (CollectionUtils.isEmpty(headerList)) { | ||
| LOG.debug("Request headers are empty. User-Agent cannot be resolved."); | ||
| return null; | ||
| } | ||
|
|
||
| List<String> forwardedUserAgent = null; | ||
| List<String> userAgent = null; | ||
| for (Header header : headerList) { | ||
| String name = header.getName(); | ||
| if (IdentityConstants.X_FORWARDED_USER_AGENT.equalsIgnoreCase(name)) { | ||
| forwardedUserAgent = header.getValue(); | ||
| } else if (IdentityConstants.USER_AGENT.equalsIgnoreCase(name)) { | ||
| userAgent = header.getValue(); | ||
| } | ||
| } | ||
|
|
||
| if (CollectionUtils.isNotEmpty(forwardedUserAgent)) { | ||
| if (LOG.isDebugEnabled() && forwardedUserAgent.size() > 1) { | ||
| LOG.debug("Multiple values found for header: " + IdentityConstants.X_FORWARDED_USER_AGENT + | ||
| ". Using the first value as the User-Agent."); | ||
|
Comment on lines
+112
to
+114
|
||
| } | ||
| return forwardedUserAgent.get(0); | ||
| } else if (CollectionUtils.isNotEmpty(userAgent)) { | ||
| if (LOG.isDebugEnabled() && userAgent.size() > 1) { | ||
| LOG.debug("Multiple values found for header: " + IdentityConstants.USER_AGENT + | ||
| ". Using the first value as the User-Agent."); | ||
|
Comment on lines
+118
to
+120
|
||
| } | ||
| return userAgent.get(0); | ||
| } | ||
|
|
||
| LOG.debug("User-Agent header not found in the request."); | ||
| return null; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing docstring for public method. All public methods should have a docstring. Consider adding: