REST is not a web services protocol. REST is a web services architectural style.
What operations can be performed on the IRIs.
GET, PUT, POST, and DELETEGET – retrieve a resourcePOST – create a new resourcePUT – edit/replace an existing resourceDELETE – remove a resourceWhat does an HTTP operation look like? A standard request from a web browser to a web server looks like:
GET / HTTP/1.1
Host: www.case.edu
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0
A common POST looks like (using URL form encoding):
POST /foo HTTP/1.1
Host: example.case.edu
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
arg1=bar&arg2=baz&arg3=quux
Another POST operation with an XML payload.
POST /foo HTTP/1.1
Host: example.case.edu
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/xml
Content-Length: 140
<parameters>
<arg1>bar</arg1>
<arg2>baz</arg2>
<arg3>quux</arg3>
</parameters>
But, that's enough about REST and HTTP. We all know what that's all about.
DSML is an XML vocabulary used to represent LDAP operations.
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry>
<dsml:attr name="givenName">
<dsml:value>Jeremy</dsml:value>
</dsml:attr>
<dsml:attr name="sn">
<dsml:value>Smith</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSN">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSNList">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="eduPersonScopedAffiliation">
<dsml:value>employee@case.edu</dsml:value>
<dsml:value>staff@case.edu</dsml:value>
</dsml:attr>
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
By using well thought out, unique IRIs for an identity;and using HTTP operations against those IRIs; we can produce a very flexible solution for manipulating and retrieving identity information.
http://webservices.case.edu/camps/jms18
Since both REST and DSML have methods for performing operations (REST uses HTTP verbs, DSML encodes it within its XML structure), a decision has to be made on where to encode that information. HTTP is the lower level protocol and has a heavier semantic value (we are, really, only using DSML as a way to serialize identity/account information in XML), so it would be best to just restrict DSML as the data representation and isolating HTTP as the protocol.
GET operation against http://webservices.case.edu/camps/jms18 returns the DSML representation of that identity.POST operation using a DSML payload against that IRI will append data.PUT operation using a DSML payload against that IRI will replace the data.DELETE will have no operation. (Identities are never "deleted.")(Don't worry. I'll explain the difference between "append" and "replace" the data.)
All right, enough with the talk, let's look at three examples.
POSTing DSML against /campsPOSTing DSML against /camps/jms18PUTing DSML against /camps/jms18POST the XML against http://webservices.case.edu/campsPOST will be the same XML document with possibly more XML elements added (such as the user's username, generated email address, unique numerical identifier, etc.).A minimal DSML entry:
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry>
<dsml:attr name="givenName">
<dsml:value>Jeremy</dsml:value>
</dsml:attr>
<dsml:attr name="sn">
<dsml:value>Smith</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSN">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSNList">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="employeeNumber">
<dsml:value>26</dsml:value>
</dsml:attr>
<dsml:attr name="eduPersonScopedAffiliation">
<dsml:value>employee@case.edu</dsml:value>
<dsml:value>staff@case.edu</dsml:value>
</dsml:attr>
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
POST it!!
#!/usr/bin/perl
my $ua = LWP::UserAgent->new(agent => 'HR System');
$ua->credentials('webservices.case.edu:443',
'ITS', 'user' => 'pass');
my $resp = $ua->request(
POST 'https://webservices.case.edu/camps' ,
Content_Type => 'application/xml+dsml',
Content => $dsmlContents);
print $resp->content();
This will end up printing out the resultant XML of the POST which is...
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry dn="uid=jms18,ou=People,o=cwru.edu,o=isp">
<dsml:attr name="givenName">
<dsml:value>Jeremy</dsml:value>
</dsml:attr>
<dsml:attr name="sn">
<dsml:value>Smith</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSN">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduSSNList">
<dsml:value>000123456</dsml:value>
</dsml:attr>
<dsml:attr name="employeeNumber">
<dsml:value>26</dsml:value>
</dsml:attr>
<dsml:attr name="eduPersonScopedAffiliation">
<dsml:value>employee@case.edu</dsml:value>
<dsml:value>staff@case.edu</dsml:value>
<dsml:value>alum@case.edu</dsml:value> <!-- Added in! -->
</dsml:attr>
<!-- All new added in to the resultant XML -->
<dsml:attr name="uid">
<dsml:value>jms18</dsml:value>
</dsml:attr>
<dsml:attr name="mail">
<dsml:value>jeremy.smith@case.edu</dsml:value>
</dsml:attr>
<dsml:attr name="mailEquivalentAddress">
<dsml:value>jms18@case.edu</dsml:value>
<dsml:value>alias@case.edu</dsml:value>
<dsml:value>alias2@case.edu</dsml:value>
</dsml:attr>
<dsml:attr name="cwruEduIdNumber">
<dsml:value>97</dsml:value>
</dsml:attr>
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
In this example, we'll add a personal mail alias (fubar@case.edu) to an account, jms18.
First, we construct the relevant XML.
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry dn="uid=jms18,ou=People,o=cwru.edu,o=isp">
<dsml:attr name="mailEquivalentAddress">
<dsml:value>fubar@case.edu</dsml:value>
</dsml:attr>
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
#!/usr/bin/perl
my $ua = LWP::UserAgent->new(agent => 'HR System');
$ua->credentials('webservices.case.edu:443',
'ITS', 'user' => 'pass');
my $resp = $ua->request(
POST 'https://webservices.case.edu/camps' ,
Content_Type => 'application/xml+dsml',
Content => $email_alias_dsml);
print $resp->content();
The result of the POST will be the user's entire DSML entry for the sake of completeness.
In this example, we'll replace a person's email aliases. We'll set them to be fubar@case.edu and bazquux@case.edu.
#!/usr/bin/perl
my $ua = LWP::UserAgent->new(agent => 'HR System');
$ua->credentials('webservices.case.edu:443',
'ITS', 'user' => 'pass');
my $resp = $ua->request(
POST 'https://webservices.case.edu/camps' ,
Content_Type => 'application/xml+dsml',
Content => qq{
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry dn="uid=jms18,ou=People,o=cwru.edu,o=isp">
<dsml:attr name="mailEquivalentAddress">
<dsml:value>fubar@case.edu</dsml:value>
<dsml:value>bazquux.edu</dsml:value>
</dsml:attr>
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
});
print $resp->content();
Follow-up discussions at https://its-wiki.case.edu/camps. Things being discussed and things to discuss:
POST operation end up correctly provisioning: