openSUSE:WebYaST REST API
REST-API - The interface between YaST-webservice and YaST-webclient
General
Yast2-webservice provides an interface to the YaST environment of the concerning host machine. This service is running with an installed webserver (e.g. lighthttp) and provides an API which is based on the REST (Representational state transfer) architecture. Please have a look to http://en.wikipedia.org/wiki/Representational_State_Transfer for more information about REST.
E.g. in order to get or set the systemtime you can use the HTTP URL:
http://<name_of_the_host>/systemtime
In order to identify if this value has to be set or has to be get the HTTP protocol has four methods:
- GET Retrieve a value
- PUT Overwrite a value
- POST Create a new value
- DELETE Delete a value. E.g. delete a user account
General Structure of the HTTP Requests
Information can be get/set in/with 3 different formats:
http://<name_of_the_host>/systemtime
Returns the systemtime in HTML format.
http://<name_of_the_host>/systemtime.xml
Returns the systemtime in XML format.
http://<name_of_the_host>/systemtime.json
Returns the systemtime in JSON format.
GET
Information can be get in a block or each entry of the block can be get in an extra HTTP call:
GET http://<name_of_the_host>/systemtime.xml
returns
<systemtime> <currenttime type="datetime">2008-09-10T17:13:37Z</currenttime> <is_utc type="boolean">false</is_utc> <timezone>Europe/Berlin</timezone> <validtimezones type="array"> <timezone> <id>Africa/Abidjan</id> </timezone> <timezone> <id>Africa/Accra</id> </timezone> .... .. . </validtimezones> </systemtime>
GET calls can be generated on the command line with the call:
curl -0 -X GET -b <cookie_file> http://0.0.0.0:3001/systemtime.xml
"-b <cookie_file>" describes a cookie with the filename "cookie_file". This cookie contains the login information of the current session. The cookie file can also be generated by a POST request:
POST
Post requests are normally used for creating new entries e.g. adding a new user account.
But they are also used for none defined request where no data will be changed. Examples here are "starting/stopping services" or generate a login session:
POST <hostname>/login
checks an SYSTEM accout and creates a session with the given values:
<hash> <login>tuxtux</login> <password>password_tuxtux</password> <remember_me type="boolean">true</remember_me> </hash>
remember_me: Generates a cookie with a valid time period of 1 day.
This returns:
<hash> <login>granted</login> </hash>
OR in the error case:
<hash> <login>denied</login> </hash>
If you are using "curl" for testing you will have to generate a session cookie with the call:
curl -0 -X POST -v -H "Content-Type: application/xml; charset=utf-8" -T <login_xml_file> -c <cookie_file> http://0.0.0.0:3001/login.xml
This call will generate a cookie file with login data described in <login_xml_file>.
PUT
Put requests are taken for OVERWRITING existing values. Multi values can be overwritten at once in a block or each entry of the block can be set explicit in an extra HTTP call:
PUT http://<name_of_the_host>/languages
with a block of XML data
<?xml version="1.0" encoding="UTF-8"?> <language> <first_language>de_DE</first_language> <second_languages type="array"> <language> <id>en_US</id> </language> <language> <id>en_GB</id> </language> </second_languages> </language>
Note: Also HTML and JSON format is a valid format for values which have to be changed.
Put calls can be generated on the command line with the call:
curl -0 -v -H "Content-Type: application/xml; charset=utf-8" -T <data-file> -b <cookie_file> http://0.0.0.0:3001/languages.xml
("-b <cookie_file>" describes a cookie with the filename "cookie_file". This cookie contains the login information of the current session.)
DELETE
Delete requests are used for deleting entries e.g. an user account:
DELETE http://0.0.0.0:3001/yast/v1/users/tux2
DELETE calls can be generated on the command line with the call:
curl -0 -X DELETE -b <cookie_file> http://0.0.0.0:3001/users/tux2
("-b <cookie_file>" describes a cookie with the filename "cookie_file". This cookie contains the login information of the current session.)
YaST webservice API
Th YaST API has two different kind of interfaces:
- Fixed interface that exists on every YaST-webservice. E.g. that are request for session handling (login,logout) or checking permissions.
- Variable interface that depends on which module/plugin (like user,patches,systemtime) is installed on the concerning YaST-webservice.
Fixed Interface
<hostname>/login
Login and creates a session. An additional login cookie will be generated if it is required. The user account will be checked with PAM. So SYSTEM users can login only. POST <hostname>/login checks an SYSTEM accout and creates a session with the given values:
<hash> <login>tuxtux</login> <password>password_tuxtux</password> <remember_me type="boolean">true</remember_me> </hash>
remember_me: Generates a cookie with a valid time period of 1 day.
This returns:
<hash> <login>granted</login> </hash>
OR in the error case:
<hash> <login>denied</login> </hash>
If you are using "curl" for testing you will have to generate a session cookie with the call:
curl -0 -X POST -v -H "Content-Type: application/xml; charset=utf-8" -T <login_xml_file> -c <cookie_file> http://0.0.0.0:3001/login.xml
This call will generate a cookie file with login data described in <login_xml_file>.
<hostname>/logout
Destroys the current session and invalidates the concerning cookie (if there is one).
POST <hostname>/logout
This returns:
<hash> <logout>Goodbye!</logout> </hash>
<hostname>/permissions
Arguments:
- user_id - Check permissions for that user
- filter - Filter for the searched permissions
This call is useful for applications which have to check the permissions BEFORE they generate a e.g. menue entry or input frame.
GET /permissions?user_id=<user_id> GET /permissions?user_id=<user_id>&filter=<filter>
returns:
<permissions type="array"> <permission> <name>org.opensuse.yast.system.time.read</name> <grant type="boolean">true</grant> </permission> <permission> <name>org.opensuse.yast.systemtime.write-timezone</name> <grant type="boolean">true</grant> </permission> <permission> <name>org.opensuse.yast.systemtime.read-isutc</name> <grant type="boolean">true</grant> </permission> <permission> <name>org.opensuse.yast.systemtime.write</name> <grant type="boolean">true</grant> </permission> ... .. . </permissions>
Variable Interface
YaST-webservice contains different plugins. Every plugin is designed for special task like user,patches,systemtime,... and has his own REST-API.
So the YaST-webservice API depends on which plugins (defined in packages) are installed.
One feature of any REST interface is that the user can ask the interface the format of the API.
YaST-webservice provides this information via the call:
GET <hostname>/resources.xml
returns:
<resources type="array"> <resource> <interface>org.opensuse.yast.system.patches</interface> <singular type="boolean">false</singular> <href>/patches</href> </resource> <resource> <interface>org.opensuse.yast.system.services</interface> <singular type="boolean">false</singular> <href>/services</href> </resource> <resource> <interface>org.opensuse.yast.system.security</interface> <singular type="boolean">true</singular> <href>/security</href> </resource> <resource> <interface>org.opensuse.yast.system.time</interface> <singular type="boolean">true</singular> <href>/time</href> </resource> <resource> <interface>org.opensuse.yast.system.language</interface> <singular type="boolean">true</singular> <href>/language</href> </resource> <resource> <interface>org.opensuse.yast.system.sambashares</interface> <singular type="boolean">false</singular> <href>/sambashares</href> </resource> <resource> <interface>org.opensuse.yast.system.users</interface> <singular type="boolean">false</singular> <href>/users</href> </resource> <resource> <interface>org.opensuse.yast.commandlines</interface> <singular type="boolean">false</singular> <href>/yast/commandlines</href> </resource> </resources>
Security
General
Login
The user (Web Application) starts a login request with user/password via Http(s)/REST to the YaST Webservice. This request will be checked with PAM . The access will be granted or denied. If the request has been successfully finished a session will be created and (on demand) a cookie will be send to the Web Application.
Single task e.g. getting system time settings
The user "tux" (Web Application) sends the Http(s) request:
GET http://<name_of_the_host>/yast/<protocol_version>/systemtime.xml
Authentification
The YaST Webservice uses Basic Authentification (Digest Auth.). This means that the username and the password are included in the HTTP header. The YaST Webservice has different levels of checking the authentification in the following order:
- The "user" and "password" in the HTTP header (Basic Auth.) will be checked with PAM.
- The field "password" contains a session ID (from a former login request) which will be compared with the current session.
- If the request includes a cookie with the session information (can be generated while the login request) this cookie will be compared whith the current session.
If ALL these checks fail an HTTP error 401 will be returned to the sender.
Request Process
The YaST Webservice checks if "tux" has the right for reading these settings: policyKit_check ("tux", "read.systemtime") If the permission has been granted the YaST Webservice starts the needed commands to the SCR - Agent in order to get all needed information: Scr.read(".sysconfig.clock.HWCLOCK") Scr.execute("/bin/date") Scr.read(".sysconfig.clock.TIMEZONE") These requests will be sent to the SCR - Agent with the user account "yastws". The SCR - Agent checks with policyKit if "yastws" has the rights for these calls.
Newer plugin release uses the YaST-DBUS interface instead of SCR calls. The method concern the checking rights are still the same here.
User Rights
Each WebYaST/YaST Webservice call has concerning access permissions which can be defined for each user. There is also the possibility to define roles (e.g. administrator, secretary, accounting,...) with special rights. These roles can be assigned to the concerning user.
Handling rights with the WebYaST-UI
The simplest way to handle permissions rights would be to use the Roles WebYaST module.
Handling rights in the command line
Each YaST Webservice call has concerning access permissions which can be defined for each user. These permissions are handled by PolicyKit in the files:
/usr/share/PolicyKit/policy/org.opensuse.yast.*
If you are getting a permission error while using the API just have a look to the YaST-webservice logfile in order to get the information which permissions are missed:
Action: org.opensuse.yast.system.users.delete User: schubi Result: no Action: org.opensuse.yast.system.users.read User: schubi Result: yes
These permissions can be set with the call "polkit-auth" like:
polkit-auth --user schubi --grant org.opensuse.yast.system.users.delete
Or can be reset by:
polkit-auth --user schubi --revoke org.opensuse.yast.system.users.read
In order to cleanup or to grant ALL permissions you can use the ruby script policyKit-rights.rb delivered with the package yast2-webservice:
Usage: policyKit-right.rb --user <user> --action (show|grant|revoke) NOTE: This program should be run by user root This call grant/revoke ALL permissions for YaST the Webservice. In order to grant/revoke single rights use: polkit-auth --user <user> (--grant|-revoke) <policyname> In order to show all possible permissions use: polkit-action
Convention
Each YaST-webservice plugin provides his own policy description file in /usr/share/PolicyKit/policy/ with following conventions:
- The filename is the plugin interface name with ".policy" postfix. E.g. the plugin "users" has the interface name "org.opensuse.yast.system.users" (defined in config/resources/users.yml). So the policy file in /usr/share/PolicyKit/policy/ has the filename org.opensuse.yast.system.users.policy
- "Less is more". In order to avoid flooding the administrator with granulated rights we should take care that in most cases one read, write, delete, new right of a plugin should be sufficient.
- If there are more granulated rights needed try to use a tree structure which means that the right described in a string is separated by "-". E.g. org.opensuse.yast.language.write-firstlanguage. The format should be "<what to do>-<with which it has to be done>"