One of my clients wanted to allow an outside reporting team to utilize an ODBC driver to pull specific data out of ServiceNow. However, due to the size of data that they would potentially pull, the IT folks wanted to restrict the user that they gave to that team such that they could only authentication for the ODBC driver query during a specific time of the day.

If this were something that was going to be used by a lot of customers, I would approach this problem by using Common Schedules as built out in the instance. I would then tailor it to basic authentication blackout periods. However, this request was more of a query rather than something that I am certain will be implemented. Since this is not a very oft requested feature, I decided to make it a simple implementation and use my own interface and logic to make this work.

The User Interface

For this feature, I created a table called “Basic Auth Time Restrictions”. I created a module for this table and placed it in the “System Security” application within the ServiceNow instance.

Once you browse to that module, you will get a list that you can populate with user records and login windows for Basic Login. Remember, the purpose of the login restrictions is to affect services and programs during authentication, not people coming into the instance via a browser.

The following is a screenshot of the list that is available in the Basic Auth Time Restriction module:

In order to restrict basic authentication for a user, simply create a “Basic Auth Time Restriction” record. When you click the “New” button, you will see the following form:

Let’s talk about each of the fields:

Schedule Active: When this checked, this login window will go into affect. When unchecked, the system ignores this window.

User: Specify a local user in the ServiceNow instance. If a user is not listed in the restrictions list, we assume that there are no restrictions for the user and they can login freely at any time.

Begin Time: The beginning time (in your current timezone) of the valid login window.

End Time: The ending time for the valid login window.

If the begin time is later than the end time, the system will assume that your times span two days. (eg: 21:00:00 – 03:00:00 is interpreted that the user can login from 9PM today through 3AM tomorrow).

A user can have multiple time restriction records. During authentication, the system will check all of the user’s restriction records to see if any of them contain a window that fits the current time. If it does, the user will be authenticated.

The Gorey Details

In order to apply these Basic Authentication login windows, I had to modify the BasicAuth script include in my instance. I deactivated the original script include and copied the script to a new BasicAuth script include.

I then added a new function to the script include called “timeRestricted(userName)”. This function takes in the userName that is being used for authentication. It then looks up the user name to get a user record in the system. Once it has the user record, it queries our “Basic Auth Time Restriction” table to find any restriction records for the given user. If no records exist in the table for the user, then it continues the authentication process.

If there are ACTIVE records in the restrictions table, the script will go through each record to see if the current time falls within the begin-end time spans. If it finds a valid window, it will allow the user to authenticate. Otherwise, it will deny user authentication with a log statement indicating that there was a time restriction for the user.

To help you understand how this was done, I have included the script here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
var BasicAuth = Class.create();

BasicAuth.prototype = {
  initialize: function (request, response, auth_type, auth_value) {
    this.request = request;
    this.response = response;
    this.auth_type = auth_type;
    this.auth_value = auth_value;
  },

  getAuthorized: function () {
    var up = Packages.com.glide.util.StringUtil.base64Decode(this.auth_value);
    var split = up.indexOf(":");

    if (split == -1) {
      gs.log("Basic authentication not well formed");
      return null;
    }

    // locate user and impersonate
    var userName = up.substring(0, split);
    var password = up.substring(split + 1);

    if (!Packages.com.glide.sys.User.authenticate(userName, password)) {
      gs.log("Basic authentication failed for user: " + userName);
      return null;
    }

    this.updateLastLogin(userName);

    /*
     * Custom code inserted to handle time restrictions
     */

    if (this.timeRestricted(userName)) {
      gs.log("User: " + userName + " is time restricted. Login Denied.");
      return null;
    }
    /*
     * End of custom Code insert block
     */


    // user is authenticated, so return it...
    return userName;
  },

  updateLastLogin: function (userName) {
    if ('true' != gs.getProperty("glide.basicauth.update_last_login_time", "false")) {
      return;
    }

    var user = new GlideRecord("sys_user");
    user.addQuery("user_name", userName);
    user.query();
    if (user.next()) {
      user.last_login_time = new GlideDateTime();
      user.update();
    }
  },

  /*
   * Custom function inserted to handle Basic Auth Time Restrictions
   */

  timeRestricted: function (userName) {
    gs.log("Checking for a time restriction on " + userName);
    var _EARLIER = -1;
    var _LATER = 1;
    var _SAME = 0;
    var user = new GlideRecord("sys_user");
    user.addQuery("user_name", userName);
    user.query();
    if (user.next()) {
      var restriction = new GlideRecord("u_basic_auth_time_restrictions");
      restriction.addQuery("u_schedule_active", "true")
      restriction.addQuery("u_user", user.sys_id);
      restriction.query();
      var restrictionsFound = 0;
      while (restriction.next()) {
        restrictionsFound++;
        var currentGmtTime = new GlideDateTime();
        currentGmtTime = new GlideDateTime(currentGmtTime.getLocalTime());

        var begin = new GlideDateTime(restriction.u_begin_time);
        begin = new GlideDateTime(begin.getLocalTime());

        var end = new GlideDateTime(restriction.u_end_time);
        end = new GlideDateTime(end.getLocalTime());


        //Check for exact same time scenario
        if (currentGmtTime.compareTo(begin) == _SAME) {
          return false;
        }
        if (currentGmtTime.compareTo(end) == _SAME) {
          return false;
        }

        //Handle case where begin and end are times within the same day
        if (end.compareTo(begin) == _LATER) {
          if ((currentGmtTime.compareTo(begin) == _LATER) && (currentGmtTime.compareTo(end) == _EARLIER)) {
            return false;
          }
        }

        //Handle case where end is before the begin time - this means they span two days
        if (end.compareTo(begin) == _EARLIER) {
          if ((currentGmtTime.compareTo(begin) == _LATER) || (currentGmtTime.compareTo(end) == _EARLIER)) {
            return false;
          }
        }
      }
      if (restrictionsFound == 0) {
        //This user has no restriction records
        return false;
      }
    }
    //Could not find any openings for this time - must be restricted
    return true;
  }
  /*
   * End of custom Code insert block
   */

}

Download the Update Set

I have provided an update set for you to download in order to use this feature for yourself. If you do choose to use this, let me know how it works for you.

Basic Auth Time Restriction Update Set