how to restrict api calls by specific time range?

Hi,

I want to implement some process that allows or restricts API Proxy calls only to certain time range. For example all the requests are allowed only time from 09:00 AM to 18:00 PM. But I have not found any sample to implement it.

The idea I've come up with so far is below:
1) Set allowable start time and allowable end time in Key-Value Map
2) Extract the request time
3) Compare time values ​​and store True/False values ​​in variable
4) Raise Fault when variable value is False

But it is not clear how to extract the request time and how to develop the comparison of values.

Any links I can refer to to implement my idea?
Or, I am wondering if there is another way to implement that process.

Solved Solved
0 3 1,372
3 ACCEPTED SOLUTIONS

Hi there,

I like your idea to implement this at a policy level. I was able to get your solution working, except I didn't use KVMs. You can find a post on how to read KVMs into JavaScript policies here.

In order to, as you put it, "extract the request time and . . . develop the comparison of values" I used a JavaScript policy with the code as shown below.

 

// Get date and time of request in UTC (milliseconds from 1970)
var reqDateTime = context.getVariable('client.received.start.timestamp');

// Generate milliseconds from 1970 for both start and end times
// Borrow date from reqDateTime
var startTime = '09:00:00'; // Time in UTC
var startDateObj = new Date(reqDateTime);
startDateObj.setHours(startTime.split(":")[0]);
startDateObj.setMinutes(startTime.split(":")[1]);
startDateObj.setSeconds(startTime.split(":")[2]);
var startDateTime = startDateObj.getTime();

var endTime = '18:00:00'; // Time in UTC
var endDateObj = new Date(reqDateTime);
endDateObj.setHours(endTime.split(":")[0]);
endDateObj.setMinutes(endTime.split(":")[1]);
endDateObj.setSeconds(endTime.split(":")[2]);
var endDateTime = endDateObj.getTime();

// Determine if we should allow this request
var allowReq = (startDateTime < reqDateTime) && (endDateTime > reqDateTime);
context.setVariable('allowReq', allowReq);

 

Then, to raise a fault in my preflow I used a conditional RaiseFault policy like this:

 

<ProxyEndpoint name="default">
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>restrict-time</Name>
      </Step>
      <Step>
        <Name>RF-Error-BadTime</Name>
        <Condition>allowReq = false</Condition>
      </Step>
    </Request>
    <Response/>
  </PreFlow>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault name="RF-Error-BadTime">
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  <FaultResponse>
    <Set>
      <Payload contentType="application/json">
        {
          "error" : {
            "code" : 400,
            "message" : "Invalid request. Please request at the appropriate time."
          }
        }
      </Payload>
      <StatusCode>400</StatusCode>
      <ReasonPhrase>Bad Request</ReasonPhrase>
    </Set>
  </FaultResponse>
</RaiseFault>

 

I hope this helps!

View solution in original post

What Raven suggested will work nicely.

Here's another option.  You can reference the variables system.time.hour and system.time.minute within the JS.  Those are relative to UTC.  So you could do something like :

 

var currentHour = context.getVariable('system.time.hour');
if (currentHour >= properties['not-after'] || currentHour < properties['not-before']) { 
 throw new Error('Restricted by time');
}

 

throwing an Error from within JavaScript will raise a fault. You can handle the fault in FaultRules.

Retrieving something from the properties collection requires that you configure your JS policy like this: 

<Javascript name='JS-Restrict-by-Time'>
  <Properties>
    <Property name='not-after'>16</Property>
    <Property name='not-before'>9</Property>
  </Properties>
  <ResourceURL>jsc://restrict-by-time.js</ResourceURL>
</Javascript>

Probably your JS logic will need to be more elaborate that what I showed there.  One could imagine having different time restrictions for different apps, where the times are attached as custom attributes on the developer app.  Or on the product. ... etc.

If your JS logic is NOT more complicated than that, then you could completely omit the JavaScript, and check the time restriction in a Condition element in  the flow language, like this: 

  <Step>
    <!-- raise a fault if the time is not in the acceptable range -->
    <Name>RF-Restricted-by-Time</Name>
    <Condition>system.time.hour GreaterThanOrEquals 16 OR system.time.hour LesserThan 9</Condition>
  </Step>

 

View solution in original post

Thanks for replies from dchiesa1 and ravenhedden.

I tried to implement like below and it works fine.

1) Set the key-value to restrict start/end time in KVM
younghunyun_0-1665014223075.png

2) Add a KVM policy to PreFlow in ProxyEndpoint for retrieving the start/end time from KVM

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="KVM-GetAllowTimes" mapIdentifier="kvm-store">
    <DisplayName>KVM-GetAllowTimes</DisplayName>
    <Properties/>
    <ExpiryTimeInSecs>300</ExpiryTimeInSecs>
    <Get assignTo="allow_hhmmss_start" index="1">
        <Key>
            <Parameter>allow_hhmmss_utc_start</Parameter>
        </Key>
    </Get>
    <Get assignTo="allow_hhmmss_end" index="1">
        <Key>
            <Parameter>allow_hhmmss_utc_end</Parameter>
        </Key>
    </Get>
    <Scope>environment</Scope>
</KeyValueMapOperations>

3) Add a JavaScript policy to PreFlow in ProxyEndpoint for comparing time is valid or not. And then set the 'T' or 'F' value to 'is_valid_time' variable.

// 'start' and 'end' values from KVM.
// 'now' value from flow variable. format: Wed, 21 Aug 2013 19:16:47 UTC
var hhmmss_start = context.getVariable('allow_hhmmss_start');
var hhmmss_end = context.getVariable('allow_hhmmss_end');
var hhmmss_now = context.getVariable('system.time').split(' ')[4];

// set variables for debug
context.setVariable("hhmmss_start", hhmmss_start.replace(/:/g,''));
context.setVariable("hhmmss_end", hhmmss_end.replace(/:/g,''));
context.setVariable("hhmmss_now", hhmmss_now.replace(/:/g,''));

if (hhmmss_start <= hhmmss_now && hhmmss_now <= hhmmss_end) {
    context.setVariable('is_valid_time', 'T');
} else {
    context.setVariable('is_valid_time', 'F');
}

4) Add a RaiseFault policy with a new ConditionalFlow in ProxyEndpoint.

<Flows>
    <Flow name="CheckValidTimeFlow">
        <Request>
            <Step>
                <Name>RF-InvalidTime</Name>
            </Step>
        </Request>
        <Response/>
        <Condition>(is_valid_time = "F")</Condition>
    </Flow>
</Flows>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-InvalidTime">
    <DisplayName>RF-InvalidTime</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="application/json">{
"ERROR": "The Valid Request Time is {allow_hhmmss_start} ~ {allow_hhmmss_end} (UTC)"
}</Payload>
            <StatusCode>601</StatusCode>
            <ReasonPhrase>Invalid Request Time</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

 

The Implementation like below:
younghunyun_3-1665014908960.png

 

As intended, If the request time is not in start/end time, the 'is_valid_time' value is set to 'F' and a Fault is raised.
younghunyun_4-1665015059848.png

younghunyun_5-1665015209050.png

Thank you again to help:)

 

 

View solution in original post

3 REPLIES 3

Hi there,

I like your idea to implement this at a policy level. I was able to get your solution working, except I didn't use KVMs. You can find a post on how to read KVMs into JavaScript policies here.

In order to, as you put it, "extract the request time and . . . develop the comparison of values" I used a JavaScript policy with the code as shown below.

 

// Get date and time of request in UTC (milliseconds from 1970)
var reqDateTime = context.getVariable('client.received.start.timestamp');

// Generate milliseconds from 1970 for both start and end times
// Borrow date from reqDateTime
var startTime = '09:00:00'; // Time in UTC
var startDateObj = new Date(reqDateTime);
startDateObj.setHours(startTime.split(":")[0]);
startDateObj.setMinutes(startTime.split(":")[1]);
startDateObj.setSeconds(startTime.split(":")[2]);
var startDateTime = startDateObj.getTime();

var endTime = '18:00:00'; // Time in UTC
var endDateObj = new Date(reqDateTime);
endDateObj.setHours(endTime.split(":")[0]);
endDateObj.setMinutes(endTime.split(":")[1]);
endDateObj.setSeconds(endTime.split(":")[2]);
var endDateTime = endDateObj.getTime();

// Determine if we should allow this request
var allowReq = (startDateTime < reqDateTime) && (endDateTime > reqDateTime);
context.setVariable('allowReq', allowReq);

 

Then, to raise a fault in my preflow I used a conditional RaiseFault policy like this:

 

<ProxyEndpoint name="default">
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>restrict-time</Name>
      </Step>
      <Step>
        <Name>RF-Error-BadTime</Name>
        <Condition>allowReq = false</Condition>
      </Step>
    </Request>
    <Response/>
  </PreFlow>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault name="RF-Error-BadTime">
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  <FaultResponse>
    <Set>
      <Payload contentType="application/json">
        {
          "error" : {
            "code" : 400,
            "message" : "Invalid request. Please request at the appropriate time."
          }
        }
      </Payload>
      <StatusCode>400</StatusCode>
      <ReasonPhrase>Bad Request</ReasonPhrase>
    </Set>
  </FaultResponse>
</RaiseFault>

 

I hope this helps!

What Raven suggested will work nicely.

Here's another option.  You can reference the variables system.time.hour and system.time.minute within the JS.  Those are relative to UTC.  So you could do something like :

 

var currentHour = context.getVariable('system.time.hour');
if (currentHour >= properties['not-after'] || currentHour < properties['not-before']) { 
 throw new Error('Restricted by time');
}

 

throwing an Error from within JavaScript will raise a fault. You can handle the fault in FaultRules.

Retrieving something from the properties collection requires that you configure your JS policy like this: 

<Javascript name='JS-Restrict-by-Time'>
  <Properties>
    <Property name='not-after'>16</Property>
    <Property name='not-before'>9</Property>
  </Properties>
  <ResourceURL>jsc://restrict-by-time.js</ResourceURL>
</Javascript>

Probably your JS logic will need to be more elaborate that what I showed there.  One could imagine having different time restrictions for different apps, where the times are attached as custom attributes on the developer app.  Or on the product. ... etc.

If your JS logic is NOT more complicated than that, then you could completely omit the JavaScript, and check the time restriction in a Condition element in  the flow language, like this: 

  <Step>
    <!-- raise a fault if the time is not in the acceptable range -->
    <Name>RF-Restricted-by-Time</Name>
    <Condition>system.time.hour GreaterThanOrEquals 16 OR system.time.hour LesserThan 9</Condition>
  </Step>

 

Thanks for replies from dchiesa1 and ravenhedden.

I tried to implement like below and it works fine.

1) Set the key-value to restrict start/end time in KVM
younghunyun_0-1665014223075.png

2) Add a KVM policy to PreFlow in ProxyEndpoint for retrieving the start/end time from KVM

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="KVM-GetAllowTimes" mapIdentifier="kvm-store">
    <DisplayName>KVM-GetAllowTimes</DisplayName>
    <Properties/>
    <ExpiryTimeInSecs>300</ExpiryTimeInSecs>
    <Get assignTo="allow_hhmmss_start" index="1">
        <Key>
            <Parameter>allow_hhmmss_utc_start</Parameter>
        </Key>
    </Get>
    <Get assignTo="allow_hhmmss_end" index="1">
        <Key>
            <Parameter>allow_hhmmss_utc_end</Parameter>
        </Key>
    </Get>
    <Scope>environment</Scope>
</KeyValueMapOperations>

3) Add a JavaScript policy to PreFlow in ProxyEndpoint for comparing time is valid or not. And then set the 'T' or 'F' value to 'is_valid_time' variable.

// 'start' and 'end' values from KVM.
// 'now' value from flow variable. format: Wed, 21 Aug 2013 19:16:47 UTC
var hhmmss_start = context.getVariable('allow_hhmmss_start');
var hhmmss_end = context.getVariable('allow_hhmmss_end');
var hhmmss_now = context.getVariable('system.time').split(' ')[4];

// set variables for debug
context.setVariable("hhmmss_start", hhmmss_start.replace(/:/g,''));
context.setVariable("hhmmss_end", hhmmss_end.replace(/:/g,''));
context.setVariable("hhmmss_now", hhmmss_now.replace(/:/g,''));

if (hhmmss_start <= hhmmss_now && hhmmss_now <= hhmmss_end) {
    context.setVariable('is_valid_time', 'T');
} else {
    context.setVariable('is_valid_time', 'F');
}

4) Add a RaiseFault policy with a new ConditionalFlow in ProxyEndpoint.

<Flows>
    <Flow name="CheckValidTimeFlow">
        <Request>
            <Step>
                <Name>RF-InvalidTime</Name>
            </Step>
        </Request>
        <Response/>
        <Condition>(is_valid_time = "F")</Condition>
    </Flow>
</Flows>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-InvalidTime">
    <DisplayName>RF-InvalidTime</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="application/json">{
"ERROR": "The Valid Request Time is {allow_hhmmss_start} ~ {allow_hhmmss_end} (UTC)"
}</Payload>
            <StatusCode>601</StatusCode>
            <ReasonPhrase>Invalid Request Time</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

 

The Implementation like below:
younghunyun_3-1665014908960.png

 

As intended, If the request time is not in start/end time, the 'is_valid_time' value is set to 'F' and a Fault is raised.
younghunyun_4-1665015059848.png

younghunyun_5-1665015209050.png

Thank you again to help:)