Middleware provides a read-write interface to the Enterprise Directory via a REST API located at https://apps.middleware.vt.edu/ws/v1. The following code sample in Python provides a quick introduction:

import jwt
import json
import requests
import time

# Base URL to Enterprise Directory Web services
base_url = 'https://apps.middleware.vt.edu/ws/v1'

# Read ED service private key used to sign authentication tokens
with open('/path/to/ed-id-private-key.pem', 'r') as f:
  key = f.read()

# Create authentication token that is valid for ten minutes
# It may be reused several times in that period
claims = {
  'iss': 'your-edid-service'
  'exp': int(time.time()) + 10 * 60
}
token = jwt.encode(claims, key, algorithm='RS256')

# Set up HTTP headers to pass the authentication token
# MUST pass these headers with all requests
headers = {'Authorization': 'Bearer ' + token}

# Query for some groups you own
response = requests.get(base_url + '/groups?uugid=chemistry.*', headers=headers)
groups = json.reads(response.text)
for group in groups:
  print group.uugid

# Set up request headers for POST requests
headers['Content-Type'] = 'application/x-www-form-urlencoded'

# Create a new group that will be nested within an existing group
requests.post(
  base_url + '/groups',
  headers=headers,
  data={'uugid': 'chemistry.chromatographers.experts', 'contact': 'alice', 'administrator': 'bob'})

# Set up request headers for PATCH requests
headers['Content-Type'] = 'application/json'

# Set group expiration date to midnight (UTC) January 1, 2020
# Syntax is described in RFC 6902 JSON Patch
patches = [
  {
    'operation': 'replace',
    'path': '/expirationDate',
    'value': 1514764800
  }
]
requests.patch(base_url + '/groups/chemistry.chromatographers', headers=headers, data=patches)

# Set up request headers for POST requests
headers['Content-Type'] = 'application/x-www-form-urlencoded'

# Add roles one at a time, but multiple requests can be pipelined easily
requests.post(
  base_url + '/groups/chemistry.chromatographers/administrators',
  headers=headers,
  data={'kind': 'person', 'id': 'alice'})
requests.post(
  base_url + '/groups/chemistry.chromatographers/managers',
  headers=headers,
  data={'kind': 'person', 'id': 'mandy'})
requests.post(
  base_url + '/groups/chemistry.chromatographers/managers',
  headers=headers,
  data={'kind': 'service', 'id': 'cos-automation'})
requests.post(
  base_url + '/groups/chemistry.chromatographers/members',
  headers=headers,
  data={'kind': 'person', 'id': 'carl'})
requests.post(
  base_url + '/groups/chemistry.chromatographers/members',
  headers=headers,
  data={'kind': 'person', 'id': 'evelyn', 'expiration', 1514764800})
requests.post(
  base_url + '/groups/chemistry.chromatographers/members',
  headers=headers,
  data={'kind': 'group', 'id': 'chemistry.chromatographers.experts'})

# Set up request headers for DELETE requests
del headers['Content-Type']

# Delete roles one at time, but mulitple requests can be pipelined easily
requests.delete(
  base_url + '/groups/chemistry.chromatographers/administrators/albert',
  headers=headers)

# Delete a group
requests.delete(base_url + '/groups/chemistry.chem101', headers=headers)

See the Operation Reference for a complete list of available Web service operations. This document provides a general discussion of Web service design and conventions and provides a detailed reference for the data structures involved.

Front Matter

Prequisites

1. Enterprise Directory Service

An ED Service provides the security principal and credential to invoke REST API operations. An ED service is typically credentialed with an X.509 certificate issued by the Middleware CA, but may also have a password credential issued under rare circumstances (see below).

Request an ED Service.

2. Authorization to Invoke REST API

An ED Service must be entitled to invoke various REST API operations. The entitlements required to invoke various operations are listed in the Operation Reference; please make note of what Web service operations you want to invoke when requesting your ED Service.

Conventions

1. URI Naming Conventions

  1. Resource URIs are named with a plural resource collection and end in a unique identifier, for example, /v1/persons/[uid], /v1/groups/[uugid].
  2. Action URIs have singular path parts and end in a verb, for example, /v1/replication/banner/consume, /v1/password/validate.

2. URIs Are Case Sensitive and Lower Case

All URIs are case sensitive and canonically lower case in terms of paths and parameter names that appear in the querystring. Parameter values that appear in the querystring may of course be upper case to support searching for case-sensitive fields like person names, but many data elements are canonically lower case as well, such as uupids and uugids and these are searched in a case-insensitive fashion.

3. JSON Data Is Case Sensitive and Mixed Case

The fields in JSON documents are named using the mixed-case convention of the Enterprise Directory LDAP schema in order to provide consistency between data elements regardless of what interface is used (LDAP vs HTTP).

Authentication

Three authentication methods are supported and are listed in order of recommended practice:

  1. HTTP Bearer token authentication via signed JWT
  2. Client TLS
  3. HTTP Basic authentication using an ED service password

Password-based authentication is discouraged; consequently ED services do not have passwords unless specifically requested and authorized by the IT Security Office.

HTTP Bearer Auth

The HTTP Bearer token authentication method provides the best balance of security and usability to clients:

  1. Strong security via digital signatures
  2. Excellent cross-platform support
  3. Precise control over token validity period

A signed JSON Web Token (JWT) provides most of the advantages above and is simply a JSON document that is prepended with a digital signature algorithm header and to which a digital signature is appended; all parts are base-64 encoded.

# Line breaks added for readability
# Parts separated by "." character
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ2dC1zZXJ2aWNlIiwiZXhwIjoxNDc2MTgxOTU5fQ.
KEZOH8CVFHuu8Pk7BmG5zBLdwk4JwORbeZMzkZ273s0hNzM16bpgyBkg8CUFdJTy5Ry6Wsu7uA0AqD9qRcakh8cWlFFZq2Jwh9XG-
Fs7xRlVRWZQL426UzxSYRI5EG203XZHeKKouS2EfaFyMUIZBjokS4Y6qe63c1eLADILDwVpegfsXkgUYg5-WdZNXSIwckKv9dQ-
cvlNwTcBJr1JlHKaDNLRr_OHGJSaZsgP7VNGdPKUcqKq0-ChsvPNVAERsgbSb-R28tTYAP8-oEl8Kk--
GPCT5-KkxNXOA9VAxX6EZYiwJbFsbgcI3EP4LkOz4E_N-Q2ownJbe71kYJc3Qg

The token above contains the following JSON document, which you can see by pasting the token (with line breaks removed) into the jwt.io debugger:

{
  "iss": "vt-service",
  "exp": 1476181959
}

The fields of the JSON document follow an established vocabulary called claims, of which two are recognized for Enterprise Directory Web services:

  1. iss - REQUIRED ED service name
  2. exp - OPTIONAL expiration date of token

The token MAY contain other claims but they will not be processed. An expiration date on the order of minutes is generally recommended. Clients MAY elect to omit the exp claim to facilitate cases where the token needs to be precomputed and cached for an indeterminate period of time.

The token MUST be signed with the private key corresponding to a valid ED service certificate and whose service exists and is active in the Enterprise Directory. The following signature algorithms are supported:

  1. RS256 (Recommended)
  2. RS384
  3. RS512

The example code above demonstrates how to construct a valid JWT for use with Enterprise Directory Web services. A python script is available to facilitate generating tokens. Example cURL usage:

export TOKEN=$(gen-bearer-token.py your-service /path/to/your-service.key)
curl -v -XGET -H"Authorization: Bearer $TOKEN" https://apps.middleware.vt.edu/ws/v1/groups/your.group

Client TLS Auth

The Enterprise Directory Web services support client TLS authentication with an ED service certificate issued by the Middleware CA, which is chained to the Virginia Tech Root CA. There are two primary configuration points for client TLS authentication:

  1. Client authentication credentials (private key + X.509 certificate containing public key)
  2. Server trusted certificates (one or more root certificates)

The authentication credentials MUST be the ED service certificate (issued by the Middleware CA) and corresponding private key. Middleware CA certificates have a 2-year validity period and must be renewed periodically. Only a single trusted CA certificate is required to communicate with the RESTful Web services:

Issuer: OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign
Validity
    Not Before: Mar 18 10:00:00 2009 GMT
    Not After : Mar 18 10:00:00 2029 GMT
Subject: OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign

-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----

The servers hosting Enterprise Directory Web services are configured for optional client authentication. If a valid client certificate is not provided in the TLS handshake, the connection will proceed with an anonymous client authentication context to allow other mechanisms such as HTTP Bearer or HTTP Basic to proceed.

HTTP Basic Authentication

Clients may authenticate with a password credential provided by HTTP Basic authentication if and only if the corresponding ED service has been configured with a generated password. The following code snippet demonstrates HTTP Basic authentication:

from requests.auth import HTTPBasicAuth
import json
import requests

response = requests.get(
  'https://apps.middleware.vt.edu/ws/v1/group?uugid=chemistry.*',
  auth=HTTPBasicAuth('vt-service', 'fakepass'))
groups = json.reads(response.text)
for group in groups:
  print group.uugid

Enterprise Directory Resources

There are 6 kinds of resources in the Enterprise Directory Web services that correspond to the branches of the Enterprise Directory LDAP schema. A resource is accessible via an HTTP request made to a URI that identifies the kind of resource and its unique identifier; thus URIs follow the convention:

/[api_version]/[resource_collection_name]/[unique_id]

Thus there are two related URIs for every resource type:

  1. Resource collection URI (create and bulk fetch resources)
  2. Resource URI (fetch, update, delete resource)

The resource URI is defined by appending the unique ID to the resource collection URI.

Resource Branch ID Resource URI Notes
Person ou=people uid /v1/persons/[uid] person demographic data
Account ou=people pid /v1/accounts/[pid] person PID account details
Mailbox ou=people address /v1/mailboxes/[address] person email account details
Service ou=services uusid /v1/services/[uusid] uusid is the service name
Group ou=groups uugid /v1/groups/[uugid] uugid is the group name
Entitlement ou=entitlements uid /v1/entitlements/[uid] uid is a numeric identifier

Optional Fields

All resources contain numerous fields, and in many cases some fields are expensive to fetch and unnecessary for the use at hand. In order to balance breadth and depth cases, many fields are marked optional and must be explicitly requested when a resource is fetched via the optional with querystring parameter included in the GET request. The syntax for requesting optional fields is follows:

/v1/[resource_collection_name]/[unique_id]?with=section1&with=section2&with=section3

where section1, section2, and section3 are sections denoting a field or field set

The names of optional field sections are documented in the resource schemas that follow and in many cases the section name is the field name since the field is naturally lowercase, otherwise a brief lowercase name is used to describe the section. The all section name is shorthand for requesting that all optional fields be included in the fetched resource. Use of this capability is discouraged for bulk queries that may return large result sets.

Person Resource

The person resource provides information about the identifying characteristics of a Virginia Tech affiliate: name, birthdate, afffiliations, employment information, and student information. It has the following schema (in YAML to allow for comments):

# Resource URI: /v1/person/14142135
{
  "uid": 14142135,
  "displayName": "Alexander Hamsandwich",
  "type": "VirginiaTech",

  # Use the Account resource for full account details
  "pid": "hammy",

  "mailPreferredAddress": "hammy@vt.edu",
  "dateOfBirth": 876215266,
  "gender": "M",
  "virginiaTechId": "987654321",

  # Optional: with=affiliations
  "affiliations": [
    "VT-ACTIVE-MEMBER",
    "VT-STUDENT",
    "VT-STUDENT-ACTIVE",
    "VT-EMPLOYEE",
    "VT-EMPLOYEE-WAGE"
  ],

  # Optional: with=mailboxes
  # Use the Mailbox resource for full email account details
  "mailboxes": [
    "hammy@vt.edu",
    "hambone@vtc.vt.edu"
  ], 

  # Optional: with=suppressions
  "suppressDisplay": false,
  "suppressions": [
    "uupid"
  ],
  "suppressibleAttributes": [
    "uupid",
    "mailPreferredAddress"
  ],

  # Optional: with=names
  "names": [
    {
      # Allowed types: BN=Banner Name, PN=Preferred Name, AN=Alumni Name
      "type": "BN",
      "prefix": "",
      "first": "Alexander",
      "middle": "Mayo",
      "last": "Hamsandwich",
      "suffix": "Jr"
    }
  ],

  # Optional: with=addresses
  "addresses": [
    {
      # LOCAL is the local address near campus
      # HOME is the address where student lived prior to enrollment
      # OFFICE is the on-campus "office" address
      "type": "LOCAL",
      "street1": "314 Progress Street",
      "street2": "Apt 5",
      "street3": "",
      "poBox": "",
      "city": "Blacksburg",
      "state": "VA",
      "zip": "24060",
      "country": "US",
      "mailStop": "",
      "phones": [
        {
          # Allowed types: HOME, LOCAL, OFFICE, CELLUAR, FAX, PAGER
          "type": "LOCAL",
          "number": "5405551212",
          "displayNumber": "540-555-1212"
        }
      ]
    }
  ],

  # Optional: with=social
  "uris": [
    {
      "label": "Facebook",
      "uri": "https://www.facebook.com/profile.php?id=12345"
    },
    {
      "label": "Google+",
      "uri": "https://plus.google.com/+AlexanderHamsandwich"
    }
  ],
  "imids": [
    {
      "label": "VT IM",
      "uri": "hammy@im.vt.edu"
    },
    {
      "label": "Google Talk",
      "uri": "hammy@google.com"
    }
  ],
  "emails": [
    {
      "type": "DSP",
      "address": "hammy@icloud.com"
    },
    {
      "type": "PRC",
      "address": "lexham@hotmail.com"
    }
  ]

  # Optional: with=certificates
  "certificates": [
    {
      "type": "X509",
      "certificate": "MIIDtTCCAp2gAwIBAgIIE...MCiAvnNZvu6HYU="
    }
  ],

  # Optional: with=employee
  "employeeData": {
    "title": "Lab Technician",
    "departments": [
      {
        "name": "Chemistry",
        "number": "000111"
      }
    ]
  },

  # Optional: with=student
  "studentData": {
    "academicLevel": "SENIOR",
    "campus": "Blacksburg",
    "college": "College of Science",
    "major": "Chemistry",
    "lastEnrollmentTerm": "Spring Semester 2000",
    "nextEnrollmentTerm": ""
  }
}

Account Resource

The account resource provides details on the person’s PID account. The account resource is intended primarily for viewing account state and managing credentials.

# Resource URI: /v1/accounts/hammy
{
  "pid": "hammy",
  "personName": "Alexander Brandon Hamsandwich",

  # Use Person resource to get person demographic data
  "personUid": 123456789,

  "creationDate": "2001-09-01:10:00-05:00",
  "expirationDate": null,

  # Optional: with=state
  # Valid states:
  #  1. ACT - Active
  #  2. LKD - Locked (commonly due to password expiration)
  #  3. SLD - Shelved (first step to deprovision; easily recoverable)
  #  4. TBR - To be released (last step to deprovision; not easily recoverable)
  #  5. PVD - Prevalidated (an account may be deleted immediately if it lingers in this state)
  "accountState": {
    "state": "ACT",
    "reason": "Userid Assigned"
  },
  "nextStateChangeDate": null,
  "passwordState": "ACT",
  "passwordChangeDate": "2016-09-02:10:00-05:00",
  "passwordExpirationDate": "2017-09-01:10:00-05:00",

  # Optional: with=recovery
  "recoveryMaintenanceDate": "2016-09-02:10:01-05:00",
  "recoveryOptions": [
    {
      # Call Virginia Tech 4-Help
      "type": "C4H",
      "identifier": "true"
    },
    {
      # OTP via SMS
      "type": "O2S",
      "identifier": "5405551212"
    },
    {
      # OTP via voice call
      "type": "O2V",
      "identifier": "5405551212"
    },
    {
      # Google linked account
      "type": "GRA",
      "identifier": "123456789987654321"
    },
    {
      # Yahoo linked account
      "type": "YRA",
      "identifier": "123456789987654321"
    }
  ]
}

Mailbox Resource

There is one mailbox resource for each type of ED email account but all share the same fields. Email accounts support aliases such that a single account may receive mail sent to multiple addresses. Additionally, forwarding addresses are supported for FE accounts such that mail sent to the account is not stored but forwarded to another address.

There are several types of ED email accounts based on the purpose and backing system:

Type Provider Description
GE Google (vt.edu) Virginia Tech email account
CE Google (vtc.vt.edu) Carilion email account
GAE Google (vt.edu) Virginia Tech auxiliary email account
CAE Google (vtc.vt.edu) Carilion auxilary email account
FE Virginia Tech (vt.edu) Virginia Tech forward-only email account

Auxiliary accounts are supplemental accounts that may be requested and granted for special circumstances. Forward only accounts are special purpose accounts that expose a specific Virgnia Tech email address for mail to be forwarded to another account, often a Virginia Tech Exchange account. Most users have only a single GE account. Carilion affiliates may have both CE and GE accounts.

Mailbox resources have the following fields:

# Resource URI: /v1/mailboxes/hammy@vt.edu
{
  # Every mailbox has an address that is its unique identifier. This is the
  # primary address of the email account.
  "address": "hammy@vt.edu",
  # Valid email account types:
  # 1. GE  - Virginia Tech account, pid-required
  # 2. GAE - Virginia Tech Auxiliary account
  # 3. CE  - Carilion account, pid-required
  # 4. CAE - Carilion Auxiliary account
  # 5. FE  - Virgina Tech Forward-Only account

  "accountType": "GE",
  "personName": "Alexander Brandon Hamsandwidh",
  "personUid": 123456789,
  "creationDate": "2016-01-01T00:00:00-05:00",
  "expirationDate": null,

  # Email system routing control allowed values:
  #  1. Google
  #  2. Exchange
  #  3. Hume
  #  4. HumeBburg
  "routeType": "Google",

  # Optional: with=state
  # Valid states:
  #  1. ACT - Active
  #  2. LKD - Locked (commonly due to password expiration)
  #  3. EXP - Expired (first step to deprovision; easily recoverable)
  #  4. SLD - Shelved (second step to deprovision; easily recoverable)
  #  5. TBR - To be released (last step to deprovision; not easily recoverable)
  "accountState": {
    "state": "ACT",
    "reason": "Email Account Added"
  },
  "nextStateChangeDate": null,
  "passwordState": "ACT",
  "passwordChangeDate": "2017-01-01T00:00:00-05:00",

  # Optional: with=addresses
  # In addition to its primary address, every email account has a preferred address.
  "preferredAddress": "hammy.alias1@vt.edu",
  # Email accounts may have up to 5 aliases by default but MAY be configured for more..
  "aliases": [ "hammy.alias1@vt.edu", "hammy.alias2@vt.edu" ],
  # FE email accounts may have 1 forwarding address by default but MAY be configured for more.
  "forwards": [ "hammy@yahoo.com", "hammy.alias1@aol.com" ],

  # The following field is write-only for purposes of password changes.
  "password": {
    "current": "thecurrentpass",
    "newpass": "thenewpassword"
  }  
}

Service Resource

An Enterprise Directory service is a credentialed account not associated with a person that is intended to be used to access and manage Enterprise Directory resources by an application, service, or system. Services have strong public key credentials by default, though they may also have complex passwords if requested and authorized by the IT Security Office. Services are explicitly authorized to view and modify data via Enterprise Directory Web services; they have the following structure.

# Resource URI: /v1/services/vt-service
{
  "uusid": "vt-service",
  "creationDate": 1476365450,
  "expirationDate": 0,
  "modificationDate": "2016-11-18T18:23:16-05:00",
  "accountState": "ACTIVE",
  "serviceDns": [
    "C=US, DC=edu, DC=vt, ST=Virginia, L=Blacksburg, O=Virginia Polytechnic Institute and State University, OU=Middleware-Client, OU=Middleware Services, SERIALNUMBER=1458240372130, CN=middleware"
  ],
  "viewablePersonAttributes": [
    "nextEnrollmentTerm",
    "mailExternalAddress",
    "accountExpirationDate",
    "personType",
    "localPager",
    "classLevelCode",
    "authId",
    "lastEnrollmentTerm",
  ],
  "webServiceAuthorizations": [
    "ed/rest/groups",
  ],
  "certificates": [                                                                                    
    "MIIG5DCCBMygAwIBAgIIC0mUzljh1dMwDQYJKoZIhvcNAQEFBQAwgZoxEzARBgoJkiaJk/IsZAEZEwNlZHUxEjAQBgoJkiaJk/IsZAEZEwJ2dDELMAkGA1UEBhMCVVMxPDA6BgNVBAoTM1ZpcmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTEkMCIGA1UEAxMbVmlyZ2luaWEgVGVjaCBNaWRkbGV3YXJlIENBMB4XDTEyMDMzMDEzNTQ0NVoXDTE0MDMzMDEzNTQ0NVowggEDMRMwEQYDVQQDDAptaWRkbGV3YXJlMRYwFAYDVQQFEw0xMzMzMDQ4NTgwMTQwMRwwGgYDVQQLDBNNaWRkbGV3YXJlIFNlcnZpY2VzMRowGAYDVQQLDBFNaWRkbGV3YXJlLUNsaWVudDE8MDoGA1UECgwzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBTdGF0ZSBVbml2ZXJzaXR5MRMwEQYDVQQHDApCbGFja3NidXJnMREwDwYDVQQIDAhWaXJnaW5pYTESMBAGCgmSJomT8ixkARkWAnZ0MRMwEQYKCZImiZPyLGQBGRYDZWR1MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu4ZaFNL7ZPO1vIMDEFtU+8SrgpEbP0VxSO5WPHiQS74yHOM4/hwHoKLm2nLdiBGNJhlLyCyowz3JRGjZvks7DVpvntZGhJ/tsewakTwEJxj5bW0l2xE4HH7NXVu5lEbM81K9ugLgW+KxS1gqqy2mJZK2xWqG2hSp6nNz2XI/5kyt+/9vSjXPpTjww52ZmOpCr3WqAUCbzwecb+OLmuClNfThNIBND/w2iV0on0tf7OllwkSaD2RZ18FZcgkDd96HGxN1MrY0vKDRlb2guF+Mi4+uYJLvo3uKwI/ibzKiQDF2M9+/xcQZv/xtD2h+vHA2yOx0lrUGDSFkjFHSsLHnECAwEAAaOCAcAwggG8MB0GA1UdDgQWBBS4fWhqHxE3xVrBVC97gyjnOuG5KjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFP02QCKa1fxb8ATJkOZPT4hLRPhOMHgGA1UdIARxMG8wDgYMKwYBBAG0aAUCAgIBMA4GDCsGAQQBtGgFAgIBATA9BgwrBgEEAbRoBQICBAEwLTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5wa2kudnQuZWR1L3Z0bXcvY3BzLzAOBgwrBgEEAbRoBQICAwEwgdIGA1UdHwSByjCBxzCBxKCBwaCBvoaBu2h0dHA6Ly92dGNhLXAuZXByb3Yuc2V0aS52dC5lZHU6ODA4MC9lamJjYS9wdWJsaWN3ZWIvd2ViZGlzdC9jZXJ0ZGlzdD9jbWQ9Y3JsJmlzc3Vlcj1DTj1WaXJnaW5pYStUZWNoK01pZGRsZXdhcmUrQ0EsTz1WaXJnaW5pYStQb2x5dGVjaG5pYytJbnN0aXR1dGUrYW5kK1N0YXRlK1VuaXZlcnNpdHksREM9dnQsREM9ZWR1LEM9VVMwCwYDVR0PBAQDAgTwMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4ICAQCQlkIY1iZAeLklJBkAg8OpCDeYzSfrTforNsqFBJSL1UHMkWCj/DzKKPPep7Gl0eEYBwfraZCPFjAWpu1Ufopn68TXF11uGyDgr+jupq7jb6K6ldi/XyzWo9CYUW4pqC3AOEvtXWKGqef2pBELZXtatasGEwxxN+D4QgAM2LQ93GAvdJ3acIlywrreA4YfrI6lCU/AtvBAJPj8YFxZdGJA32lZw4XQWfNqhGsT7fWVZvlFtO9+G4V8kskdWvE84+RCeg6u+mAAcdLdbUzj4DOW1kBa0m843hhc2z5PdNtyJF6UIKdr/xYRydG/hnlUfN26cUzmVkKiDXko7MUF1Makmz9497I8J3eOIF9I+O59ptuVKHOHMsfog0IijYYFouyFH5NJZlPaIwTStrUj4X0IuoARyYMBZrF7jaWOi+9cYKj+YN77PQ9qVga75XdQZLK7Hcu5AHbP1bwebAu7YBUqyYHMOypzRwgzCSC36Eh8E25nhwH7kyKJYU+GduuPdVl2M+R4SEfKzW0bYwMAma3TDHoWMnlR++FjDwB+x+/bkHvRPCfPt+MHAS6t5b5t6CGRlo8DhEIjcgmfua683qaN2Xkqp8lsf+7CeKCI6IBiVjTieP+ly9QAoDaLzLEl42gpH3xMeIynEhX+MnzDFfSY5S/ZItKKYat0ghxJ+vzEIw=="
  ],

  # Optional: with=contacts
  contacts: [
    {
      "kind": "person",
      "uid": 111,
      "pid": "alice",
      "displayName": "Alice Alyssonian"
    },
    {
      "kind": "person",
      "uid": 222,
      "pid": "bob",
      "displayName": "Bob Bobberann"
    }

  # Optional: with=administrators
  administrators: [
    {
      "kind": "person",
      "uid": 333,
      "pid": "alice",
      "displayName": "Alice Alyssonian"
    },
    {
      "kind": "person",
      "uid": 444,
      "pid": "bob",
      "displayName": "Bob Bobberann"
    }
  ]
}

Group Resource

An Enterprise Directory group is a container for people, services, and groups that may be used by its members for communication, collaboration, and/or authorization. Groups have a controlled namespace where each namespace part is separated by a dot. The first namespace part is called the stem and must be created via service request; groups are subsequently self-service with respect to creation and membership management. Groups have 5 roles associated with them:

Role View Members Manage Members Create Groups Designate Managers Email Notifications
Member          
Administrator X X X X X
Manager X X      
Contact         X
Viewer X        

All roles except viewer may be fulfilled by either an account (i.e. person) or a service (i.e. machine) to allow both human users and software agents to communicate, collaborate, and participate in authorization. Only services may fulfill the viewer role, which is intended to permit a software agent to see the group when its suppression flags are enabled. Groups are private by default but may be made public by toggling suppression flags, suppressDisplay and suppressMembers. If these flags are enabled, the group and its members are hidden from anonymous LDAP queries against the ou=groups branch of the Enterprise Directory. Suppressed groups can be made visible by setting a service viewer and using that service principal to bind and query the directory.

Groups have expiration dates by default to encourage active maintenance; email notifications include periodic email expiration notifications at a minimum. Group members (in any of the roles above) MAY have expiration dates. A common use case for member expiration is to enforce temporary authorization policy on a party whose affiliation has a known end-date; for example, end of term for a student participating in a research project.

Groups have the following structure.

# Resource URI: /v1/groups/chemistry.chromatographers
{
  "uugid": "chemistry.example",
  "displayName": "Gas Chromatograph Operators",
  "creationDate": 1476365450,
  "expirationDate": 1507901499,

  # Optional: with=replication
  # Whether the group is replicated to the Google Apps vt.edu domain
  "googleGroup": false,
  # Whether this group should replicate to VT Active Directory
  "activeDirectoryGroup": false,

  # Optional: with=suppression
  "suppressDisplay": false,
  "suppressMembers": false,

  # Optional: with=social
  "emailAddress": "chromatographers@vt.edu",
  "labeledUris": [
    "Home Page:http://www.chem.vt.edu/chromatography"
  ],

  # Optional: with=contacts
  contacts: [
    {
      "kind": "person",
      "uid": 111,
      "pid": "mallory",
      "displayName": "Mallory Noe-Payne Nogaine",
      "creationDate": 1476365450,
      "expirationDate": 0
    }
  ],

  # Optional: with=administrators
  administrators: [
    {
      "kind": "person",
      "uid": 222,
      "pid": "alice",
      "displayName": "Alice Alyssonian",
      "creationDate": 1476365450
    },
    {
      "kind": "person",
      "uid": 333,
      "pid": "bob",
      "displayName": "Bob Bobberann"
      "creationDate": 1476377549
    },
    {
      "kind": "service",
      "uusid": "chem-automation",
      "creationDate": 1476377549,
      "expirationDate": 1507913549
    }
  ]

  # Optional: with=managers
  managers: [
    {
      "kind": "person",
      "uid": 444,
      "pid": "carla",
      "displayName": "Carla Carlottington",
      "creationDate": 1476365450,
      "expirationDate": 1507901499
    }
  ],

  # Optional: with=viewers
  # Typically these are services
  viewers: [
    {
      "kind": "service",
      "uusid": "cos-toolkit",
      "creationDate": 1476377549,
      "expirationDate": 1507913549
    }
  ],

  # Optional: with=members
  # Members may be any of the following: account, person, or service
  members: [
    {
      "kind": "person",
      "uid": 555,
      "pid": "david",
      "displayName": "David Davidian",
      "creationDate": 1476377549,
      "expirationDate": 1507913549
    },
    {
      "kind": "person",
      "uid": 666,
      "pid": "eloise",
      "displayName": "Eloise Elysian",
      "creationDate": 1476377549,
      "expirationDate": 1507913549
    },
    {
      "kind": "group",
      "uugid": "chemistry.chromatographers.experts",
      "displayName": "Chromatography Experts Group",
      "creationDate": 1476377549,
      "expirationDate": 1507913549
    }
  ],

  # Optional: with=membership
  # These are the IDs of groups of which this one is a member
  # This field is read-only
  membership: [
    "chemistry"
  ]
}

Entitlement Resource

An Enterprise Directory entitlement is an authorization granted to an account or service to access data or perform an action. An ED service belonging to an application, service, or system grants the entitlement to an account or service and interprets its meaning when the grantor presents its. Entitlements have 4 associated roles:

  1. Manager - the service that created the entitlement
  2. Member - the account or service to which the entitlement was granted
  3. Sponsor - optional account (owned by a person) that sponsored granting the entitlement
  4. Viewer - one or more arbitary third party services that may view the entitlement

Entitlements are naturally sensitive and may be viewed only by the parties involved; third parties may view entitlements only when explicitly permitted via the viewers field.

Entitlements have an open namespace, but are encouraged to following the URN specification. A URI, which is a specialization of a URN, has a good balance of structure and flexibility to accommodate most cases. Consider the following example:

factory/widgets/activate?field=value

The entitlement above belongs to the factory application and entitles the grantor to perform the activate action of the widgets section with data field=value. It’s a contrived example, but explains how entitlements can cover both actions and data.

Entitlements have the following structure.

# Resource URI: /v1/entitlement/factory%2Fwidgets%2Factivate%3Ffield%3Dvalue
# The entitlement name MUST be URL encoded when it appears in the resource URI.
{
  "name": "factory/widgets/activate?field=value",
  "creationDate": 1476365450,
  "expirationDate": 1507901499,

  # Member may be either an account or a service
  # An account is demonstrated here.
  "member": {
    "kind": "person",
    "uid": 111,
    "pid": "bob",
    "displayName": "Bob Bobberann"
  },

  "manager": {
    "kind": "service",
    "uusid": "vt-service"
  },

  # Optional: with=sponsor
  "sponsor": {
    "kind": "person",
    "uid": 222,
    "pid": "alice",
    "displayName": "Alice Alyssonian"
  },

  # Optional: with=viewers
  "viewers": [
    {
      "kind": "service",
      "uusid": "some-other-service"
    }
  ]
}

Entitlements are immutable except for the following fields that may be modified:

  1. expirationDate
  2. viewers

The entitlement should be removed and recreated if other fields require modification.

Operations on Resources

Resources can be created, retrieved, updated, and deleted by varying the HTTP method used in an HTTP request:

  1. GET - fetch one or more resources
  2. POST - create a new resource
  3. PATCH - update a resource with merge semantics
  4. DELETE - delete a resource

A GET request to the resource collection URI with query parameters is used for a bulk query while a GET to the resource URI is used to fetch a single resource. The following code sample demonstrates the distinction:

# Get a list of Chemistry groups using a "starts with" query and sort results by uugid field
response = requests.get(base_url + '/v1/groups?uugid=chemistry.*&sort=uugid', headers=headers)

# Get the PID account for user hizzy
response = requests.get(base_url + '/v1/accounts/hizzy/pid')

Querying for Resources

Querystring parameters are used for selecting and limiting bulk query results; these are particular to each type of resource and are discussed in detail in relevant sections of the operation reference. Query parameters are documented in mixed case, but are case-insensitive in practice. As a general rule, the fields that would be expected to be used for searching are supported for searching; for example, selecting groups by manager role or selecting persons by department/organizational unit.

Multiple fields may specified for selecting and filtering bulk queries, and they are combined into a single logicial filter expression as follows:

  • Distinct fields are combined with an AND expression unless otherwise noted (e.g. group roles)
  • Repeat occurrences of the same field are combined with an OR expression

An example should clarify the behavior. Suppose a client wishes to search for all groups that start with either chemistry.nmr or chemistry.chromatography and are managed or administered by user bob. A GET request to the following URL would produce the desired set of groups:

/v1/group?uugid=chemistry.nmr*&uugid=chemistry.chromatography*&manager=bob&administrator=bob

It’s important to note that group roles have OR semantics, so the distinct manager and administrator fields combine as needed to query for groups in which bob is in either role.

Query Result Pagination

A pagination feature supports fetching results in batches of arbitrary size. There are two parameters to control the offset and size of results returned:

  1. start - Starting position in result set; first result is 1
  2. stop - Ending position in result set

Both values are inclusive, thus to fetch results 51 through 100 the following request would be used:

/v1/group?uugid=chemistry.*&manager=bob&start=51&stop=100

Creating a New Resource

The POST method MUST be used for resource creation by making a request to the resource collection URI. All parameters used to create a new resource are provided via application/x-www-form-urlencoded. The following example demonstrates resource creation:

# Create group chemistry.chromatographers, which requires both an administrator and contact
payload = {
  'uugid': 'chemistry.chromatographers',
  'contact': 'connie',
  'administrator': 'albert'
}
headers['Content-Type'] = 'application/x-www-form-urlencoded'

# Response contains empty body and resource URI in Location header
response = requests.post(base_url + '/v1/groups', headers=headers, data=payload)
resource_uri = response.headers['Location']

# Further customize the new group via PATCH requests to the resource URI as described below

When a resource is created, a 201 Created response is returned with the newly-created resource URI in the Location response header. A separate request to that URI should be made to fetch the new entity.

Updating an Existing Resource

Update a resource by sending a PATCH request to the resource URI containing a JSON document in the entity body. The JSON document should contain fields of the resource that require updates. The special value null may be specified in a PATCH request to indicate that a field value should be removed (if allowed). In either case some fields are immutable; these are clearly marked in the resource schema sections. An attempt to modify an immutable field results in a 400 Bad Request response.

For fields that contain collections of items, there is a special syntax for PATCH operations that determines whether items are added, removed, or replaced:

  1. ”+” prefix indicates to add an item
  2. ”-“ prefix indicates to remove an item
  3. No prefix indicates that data on an existing item should be modified (e.g. expiration dates)

The following example should help clarify collection handling syntax.

headers = {
  'Authorization': 'Bearer ' + token,
  'Content-Type': 'application/json'
}

# Add administrator bob and remove eve.
# Add manager manny and update expiration of manager mandy.
payload = {
  'administrators': [
    {'uupid': '+bob'},
    {'uupid': '-eve'}
  ],
  'managers': [
    {
      'uupid': '+manny',
      'expirationDate': '2017-01-01T00:00:00-05:00'
    },
    {
      'uupid': 'mandy',
      'expirationDate': '2017-01-01T00:00:00-05:00'
    }
  ]
};
requests.patch(base_url + '/group/chemistry.chromatographers', headers=headers, data=payload)

Date Handling

Date fields are represented as ISO 8601 date strings of the format yyyy-MM-dd'T'hh:mm:ssX, where X is the offset in +/-hh:mm syntax. Dates are always reported in the US/Eastern time zone where the main Virginia Tech campus resides, which allows clients to ignore the time zone when they are in the same locale and thereby simplify date handling. Some examples of dates provided by GET operations:

2016-11-30T07:30:01-05:00 (07:30:01AM November 30, 2016 EST)
2017-01-01T00:01:20-05:00 (12:01:20AM January 1, 2017 EST)
2017-05-05T17:45:15-04:00 (5:45:15PM May 5, 2017 EDT)

Clients SHOULD provide dates with a time zone when setting dates; if the time zone is omitted, then the US/Eastern time zone is assumed. All of the following may be used to indicate the date 12:30:00PM January 1, 2017 EST:

2017-01-01T12:30:00
2017-01-01T12:30:00-05:00
2017-01-01T07:30:00Z

Clients MAY provide dates as an integer for convenience, which will be treated as a Unix timestamp measured in seconds since 1970-01-01T00:00:00Z. Note that this may produce unexpected results as the time will be converted by the system to US/Eastern time, resulting in times that are off by 4 or 5 hours unless care is taken to accommodate for time zones on the client system. In many cases, however, such rough accuracy is acceptable when handling expiration dates and similar data elements in the Enterprise Directory.