+ Start a Discussion
J KeenerJ Keener 

Best Practices to determine whether your connected to an Intranet when drawing a Visualforce Page

I've been tasked with putting a number of tabs into Salesforce.com that access internal intranet sites via web links.  These sites will obviously not work correctly if someone is connecting outside of our intranet.  What I would like to do, is proactively present the users a message if they go to this tab that tells them something like: "To use this functionality, you need to have access to the Intranet.  To do so, please run your xyz vpn software and then return to this tab."

What I'm brainstorming is how to tell if a user is on our intranet, so I can perform the above.

Option 1, would be to somehow either ping, or connect to the page and if it errors, capture the error somehow and present my message.

Option 2, would be to look at the users originating IP Address.  In our case, we are coming from behind a proxy, so everyone's IP address appears to Salesforce as the same IP if they are connected to our intranet. 

Looking at those two options, Option 2 seems the most feasible.  Unfortunately, I don't see a way in APEX to obtain the users originating IP address.  I'm wondering if I am missing something, or if maybe the right answer to obtaining the IP address might be to include some additional Javascript in the VF page to try to obtain it. 

If anyone has done something similar, please let me know!

Thanks!

Jon Keener
jhkeener@ashland.com

J KeenerJ Keener
UPDATE:

I think I am getting close to a solution for obtaining the originating IP.  I've been able to get to it from the VF/Javascript side, but I have not been able to push the value automatically into an APEX variable in the VF Page Controller.  My goal is for this IP Address to be available to the page prior to the page being totally drawn, so that I can use to to make decisions on how to draw the page.  The current code is below:

At the top of my Visualforce page, right after the APEX Page markup, I inserted the following Javascript function:  (I found a variation of this in /js/functions.js)

Code:
<SCRIPT LANGUAGE="JavaScript">
function getclientSrcCookieValue()
{
    var c = document.cookie;
    var idx = c.indexOf('clientsrc=');
    if ( idx == -1) return "";
    idx += 'clientsrc='.length;
    var end = c.indexOf(';',idx);
    if ( end == -1) end = c.length;
    return c.substring(idx,end);
}
</script>

 

Right after the APEX Form, I included the following in the VF Page: (if I place an alert in there, it confirms that the JS function is returning the IP Address)

Code:
<apex:inputHidden value="{!IPAddress}" id="IPAddress"/>
<SCRIPT LANGUAGE="JavaScript">document.getElementById('{!$Component.IPAddress}').value=getclientSrcCookieValue();</script>


I also inserted the following in the VF Page farther down to try to output the value of the IPAddress.

Code:
<apex:outputText value="IPAddress="/>
<apex:outputText value="{!IPAddress}"/>

 
In the Controller, I placed the following code:

Code:
private static String IPAddress = '';

public String getIPAddress() {
 return IPAddress;
}
 
public void setIPAddress(String dataIn) {
 IPAddress = dataIn;
}

 


Unfortunately, when the VF page runs and hits the outputText markup, the variable IPAddress is still returning a blank string.  I'm assuming that this is due to the page not yet initiating the call to the set method.  I've been trying numerous things to get it into the variable, but so far, I've had no luck.

Another option that I don't think is possible is to implement the rules in the Javascript on the VF page.  One option that might work is performing a redirect in the Javascript to a different static VF page with my error message on it if the function returns an invalid/non-Intranet IP.

Jon Keener
jhkeener@ashland.com


 




Message Edited by J Keener on 07-11-2008 12:56 PM
J KeenerJ Keener
ANOTHER UPDATE:

Performing the redirect in JavaScript seems to be the best way to accomplish this, and it appears to work.

I added the following to the JavaScript I had shown above.  (the function is the same, just the addition of the if statement)

Code:
<SCRIPT LANGUAGE="JavaScript">

function getclientSrcCookieValue()
{
    var c = document.cookie;
    var idx = c.indexOf('clientsrc=');
    if ( idx == -1) return "";
    idx += 'clientsrc='.length;
    var end = c.indexOf(';',idx);
    if ( end == -1) end = c.length;
    return c.substring(idx,end);
}

if (getclientSrcCookieValue() != '999.999.999.999') 
 {
 window.location = '/apex/Outside_of_Intranet_Error';
 }

</SCRIPT>

 
In the other VF Page called "Outside_of_Intranet_Error", I currently have just the following, mainly to prove it works.

Code:
<apex:page>
<h1>Sorry, but this content is only accessible via the Intranet.  Please connect to the Company Intranet and retry.</h1>
</apex:page>

 
So I think this is going to work.  Not exactly the most perfect solution, but for this scenario, it'll do what I need to do to provide a clean error message to my users.

One key Item to note: using the "document.cookie" in the JavaScript could cause this to break in the future, since Salesforce could change the content of the cookie, or rename the variable,clientSrc, that holds the IP information.  That being said, it's a better solution than no solution.

Hopefully someone else finds this useful too.

Jon Keener
jhkeener@ashland.com



jwetzlerjwetzler
Have you looked at all at using actionFunction?  It might give you what you need.

Here's a crappy page I put together, change the isVPN to false to get it to show you the error message.

Code:
<apex:page controller="actionCon">
  <apex:form >
    <apex:actionFunction name="show" action="{!showVPN}" rerender="thePanel"/>
    <apex:actionFunction name="hide" action="{!hideVPN}" rerender="thePanel"/>   
  </apex:form>

  <script>
    var isVPN = function() {
      return true;
    }
  
    if (isVPN()) show();
    else hide();
  </script>
  
  <apex:outputPanel id="thePanel">
    <apex:outputPanel rendered="{!!isVPN}" id="noVPN">
      You're not logged into the VPN!
    </apex:outputPanel>
  
    <apex:outputPanel rendered="{!isVPN}" id="yesVPN">
      Congratulations, you are on the VPN
    </apex:outputPanel>
  </apex:outputPanel>
</apex:page>

 
Code:
public class actionCon {
    boolean isVPN = false;

    public boolean getIsVPN() {
        return isVPN;
    }
    

  public PageReference showVPN() {
    isVPN = true;
    return null;
  }
  
  public PageReference hideVPN() {
    isVPN = false;
    return null;
  }
}

 There might be a slightly cleaner way to do this (and you should be able to do full page redirects instead of just rendering outputPanels) but I just threw it together to see if it would work.

Also w.r.t. your posted solution, it's a better idea to reference {!$Page.Outside_of_Intranet_Error} than to hardcode the URL.
dchasmandchasman
Jon - this might help - I just whipped up a small demo of getting HTTP headers/query params using a small amount of apex code - all server side. Are there any headers that your corporate proxy sets today that we could use as a sentinel value? If not, can you get a header added?
/apex/httpInfoDemo:
<apex:page controller="ClientInfoDemo">
    <apex:pageBlock title="Client Info">
        <apex:pageBlockSection title="URL">
        URL: {!url}<br/>
        </apex:pageBlockSection>
        <c:displayMap title="Parameters" value="{!parameters}"/>
        <c:displayMap title="Headers" value="{!headers}"/>
    </apex:pageBlock>      
</apex:page>
 
/apexcomponent/displayMap:
<apex:component>
    <apex:attribute name="value" type="NameValuePair[]" description="TODO: Describe me"/>
    <apex:attribute name="title" type="String" description="TODO: Describe me"/>
    <apex:pageBlockSection title="{!title}">
        <apex:pageblockTable value="{!value}" var="v">
            <apex:column headerValue="Name" value="{!v.name}"/>
            <apex:column headerValue="Value" value="{!v.value}"/>
        </apex:pageblockTable>
    </apex:pageBlockSection>
</apex:component>
 
Supporting Apex classes:
public class ClientInfoDemo {
public List<NameValuePair> getHeaders() {
return convertToNameValueList(ApexPages.currentPage().getHeaders());
}

public List<NameValuePair> getParameters() {
return convertToNameValueList(ApexPages.currentPage().getParameters());
}

public String getUrl() {
return ApexPages.currentPage().getUrl();
}

private List<NameValuePair> convertToNameValueList(Map<String, Object> m) {
List<NameValuePair> result = new List<NameValuePair>();

for (String key : m.keySet()) {
result.add(new NameValuePair(key, m.get(key)));
}

return result;
}
}

public class NameValuePair {
public NameValuePair(String name, Object value) {
this.name = name;
this.value = value;
}

public String name { get; private set; }
public Object value { get; private set; }
}
 


Message Edited by dchasman on 07-12-2008 03:22 AM

Message Edited by dchasman on 07-15-2008 10:49 AM