fasjson-client

A python client library for the FASJSON API

This client uses the bravado library to build dynamic api methods based on open-api specs (version 2.0).

Usage

Instantiate the client with the FASJSON URL you want to use:

>>> from fasjson_client import Client
>>> c = Client('http://fasjson.example.com')
>>> c.whoami().result
{'dn': 'uid=admin,cn=users,cn=accounts,dc=example,dc=test', 'username': 'admin', 'service': None, 'uri': 'http://fasjson.example.test/fasjson/v1/users/admin/'}

Authentication

Authentication is done with Kerberos. If you want to explicitely specify a principal to authenticate as, use the principal constructor argument:

c = Client('http://fasjson.example.com', principal='admin@EXAMPLE.TEST')

Configuring an application for Kerberos authentication

Users authenticate via kinit, applications authenticate via keytabs. It is highly recommended to use gssproxy in order to keep your keytabs secure.

  • First, install gssproxy with dnf install gssproxy

  • Create the service that you want to authenticate as in IPA: ipa service-add SERVICE/host-fqdn (for example ipa service-add HTTP/server.example.com)

  • Get the keytab for that service and store it in gssproxy’s directory: ipa-getkeytab -p SERVICE/host-fqdn -k /var/lib/gssproxy/service.keytab (for example ipa-getkeytab -p HTTP/server.example.com -k /var/lib/gssproxy/httpd.keytab)

  • Add a configuration file for your service in gssproxy’s configuration directory:

    # /etc/gssproxy/50-servicename.conf
    
    [service/servicename]
      mechs = krb5
      cred_store = keytab:/var/lib/gssproxy/service.keytab
      cred_store = client_keytab:/var/lib/gssproxy/service.keytab
      allow_constrained_delegation = true
      allow_client_ccache_sync = true
      cred_usage = both
      euid = user_the_service_runs_as
    

For example:

# /etc/gssproxy/80-httpd.conf

[service/httpd]
  mechs = krb5
  cred_store = keytab:/var/lib/gssproxy/httpd.keytab
  cred_store = client_keytab:/var/lib/gssproxy/httpd.keytab
  allow_constrained_delegation = true
  allow_client_ccache_sync = true
  cred_usage = both
  euid = apache
  • Restart gssproxy with systemctl restart gssproxy

  • Configure the service to run with the GSS_USE_PROXY environment variable set. Services started by systemd can be configured with a service configuration file, for example with the httpd service:

    # /etc/systemd/system/httpd.service.d/gssproxy.conf
    # /usr/lib/systemd/system/httpd.service.d/gssproxy.conf
    
    [Service]
    Environment=KRB5CCNAME=/tmp/krb5cc-httpd
    Environment=GSS_USE_PROXY=yes
    

Your service should now be able to authenticate with Kerberos

Pagination

Some operations can be paginated:

>>> from fasjson_client import Client
>>> c = Client('http://fasjson.example.com')
>>> response = c.list_users(page_size=2)
>>> response.result
[{'username': 'user1', [...]}, {'username': 'user2', [...]}]

The pagination data is available in the page property:

>>> response.page
{'total_results': 52, 'page_size': 2, 'page_number': 1, 'total_pages': 26}

Next and previous pages are available with the next_page() and prev_page() methods, which return the same class of objects:

>>> response.next_page().result
[{'username': 'user3', [...]}, {'username': 'user4', [...]}]

Selecting attributes

You can select which attributes you want to get from the server using the X-Fields header. The header is given as a list of attribute names:

>>> from fasjson_client import Client
>>> c = Client('http://fasjson.example.com')
>>> response = c.list_users(
...     page_size=1,
...     _request_options={
...         "headers": {"X-Fields": ["username", "emails"]}
...     }
... )
>>> response.result
[{'username': 'user1', 'emails': ['user1@example.com']}]

Command line

This package also provides a command-line client to do some operations. Install the dependencies with poetry install -E cli and run fasjson-client --help to see which operations are available.

Migrating from python-fedora

Fasjson-client provides functionality for the most important endpoints previously exposed in python-fedora. Below is a list of common python-fedora endpoints and their fasjson-client alternatives.

Creating a client

The instantiation of a client can be done similarly to python-fedora, except there is no need to provide a username and password with which to authenticate. Instead, this authentication is performed by your service via Kerberos.

For more information, please see Usage. The content below assumes you have setup your client as detailed in Usage.

Pagination is supported in some of the fasjson_client API calls listed below, for more information about how to use it see Pagination.

The following sections are divided by the objects requested, and each corresponding python-fedora method is listed as a heading - with the appropriate fasjson-client endpoint then explained below.

Groups

group_by_id

You must now use the groupname instead of id.

>>> client.get_group(groupname="testGroup").result
{'groupname': 'testGroup', 'uri': 'http://fasjson.example.test/fasjson/v1/groups/testGroup/'}

group_by_name

>>> client.get_group(groupname="testGroup").result
{'groupname': 'testGroup', 'uri': 'http://fasjson.example.test/fasjson/v1/groups/testGroup/'}

group_members

>>> client.list_group_members(groupname="testGroup", page_size=5).result
[{'username': 'user1', [...]}, {'username': 'user2', [...]}]

People

person_by_id

You must now use the person’s username instead of id

>>> client.get_user(username="test").result
{'username': 'test', 'surname': 'user', 'givenname': 'test', 'emails': ['test@example.test'], 'ircnicks': ['test', 'test_1'], 'locale': 'en-US', 'timezone': None, 'gpgkeyids': None, 'certificates': None, 'creation': None, 'locked': False, 'uri': 'http://fasjson.example.test/fasjson/v1/users/test/'}

person_by_username

>>> client.get_user(username="test").result
{'username': 'test', 'surname': 'user', 'givenname': 'test', 'emails': ['test@example.test'], 'ircnicks': ['test', 'test_1'], 'locale': 'en-US', 'timezone': None, 'gpgkeyids': None, 'certificates': None, 'creation': None, 'locked': False, 'uri': 'http://fasjson.example.test/fasjson/v1/users/test/'}

user_data

>>> client.list_users(page_size=50).result
[{'username': 'user1', [...]}, {'username': 'user2', [...]}]

people_by_groupname

>>> client.list_group_members(groupname="testGroup", page_size=5).result
[{'username': 'user1', [...]}, {'username': 'user2', [...]}]

Getting all items at once

The list_all_entities method is an iterator over all records of an entity in FASJSON, for example users or groups. They will be retrieved in multiple server calls (using pagination). You can specify the number of users that should be returned in each server call in the page_size argument if you have performance issues, but the default should be fine. An example with users:

>>> for user in client.list_all_entities("users", page_size=1000):
...     print(user)
{'username': 'user1', [...]}
{'username': 'user2', [...]}
{'username': 'user3', [...]}
[...]

Contributing

Development

Install dependencies:

poetry install

Run the tests:

tox

License

Licensed under lgpl-3.0