Source code for RestAuthClient.group
# -*- coding: utf-8 -*-
#
# This file is part of RestAuthClient (https://python.restauth.net).
#
# RestAuth is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RestAuth is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with RestAuth. If not, see <http://www.gnu.org/licenses/>.
"""
Module handling code relevant to group handling.
.. moduleauthor:: Mathias Ertl <mati@restauth.net>
"""
import sys
try:
from RestAuthClient import common, restauth_user
from RestAuthClient.error import *
except ImportError:
import common, restauth_user
from error import *
try:
from RestAuthCommon import error
except ImportError:
print( "Error: The RestAuthCommon library is not installed." )
sys.exit(1)
if sys.version_info < (3, 0):
import httplib as http
else:
from http import client as http
[docs]def create( conn, name ):
"""
Factory method that creates a *new* group in RestAuth.
:param conn: A connection to a RestAuth service.
:type conn: :py:class:`.RestAuthConnection`
:param name: The name of the group to create
:type name: str
:return: The group object representing the group just created.
:rtype: Group
:raise BadRequest: If the server was unable to parse the request body.
:raise Unauthorized: When the connection uses wrong credentials.
:raise GroupExists: When the user already exists.
:raise UnsupportedMediaType: The server does not support the
content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
resp = conn.post( Group.prefix, { 'group': name } )
if resp.status == http.CREATED:
return Group( conn, name )
elif resp.status == http.CONFLICT:
raise GroupExists( "Conflict." )
elif resp.status == http.PRECONDITION_FAILED:
raise error.PreconditionFailed( resp )
else:
raise UnknownStatus( resp )
[docs]def create_test( conn, name ):
"""
Do a test-run on creating a new group (i.e. to test user input against the RestAuth server
configuration). This method throws the exact same Exceptions as :py:func:`create` but always
returns None instead of a :py:class:`Group` instance if the group could be created that way.
.. NOTE:: Invoking this method cannot guarantee that actually creating this group
will work in the future, i.e. it may have been created by another client in the meantime.
"""
resp = conn.post( '/test/%s/'%Group.prefix, { 'group': name } )
if resp.status == http.CREATED:
return True
elif resp.status == http.CONFLICT:
raise GroupExists( "Conflict." )
elif resp.status == http.PRECONDITION_FAILED:
raise error.PreconditionFailed( resp )
else:
raise UnknownStatus( resp )
[docs]def get_all( conn, user=None ):
"""
Factory method that gets all groups for this service known to
RestAuth.
:param conn: A connection to a RestAuth service.
:type conn: :py:class:`.RestAuthConnection`
:param user: Only return groups where the named user is a member
:type user: str
:return: A list of Group objects
:rtype: List of :py:class:`groups <.Group>`
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: When the given user does not exist.
:raise NotAcceptable: When the server cannot generate a response
in the content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
params = {}
if user:
if user.__class__ == restauth_user.User:
user = user.name
params['user'] = user
resp = conn.get( Group.prefix, params )
if resp.status == http.OK:
body = resp.read().decode( 'utf-8' )
names = conn.content_handler.unmarshal_list( body )
return [ Group( conn, name ) for name in names ]
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs]def get( conn, name ):
"""
Factory method that gets an *existing* user from RestAuth. This
method verifies that the user exists in the RestAuth and throws
:py:exc:`.ResourceNotFound` if not.
:param conn: A connection to a RestAuth service.
:type conn: :py:class:`.RestAuthConnection`
:param name: The name of the group to get
:type name: str
:return: The group object representing the group in RestAuth.
:rtype: :py:class:`.Group`
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group does not exist.
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
resp = conn.get( '%s%s'%(Group.prefix, name) )
if resp.status == http.NO_CONTENT:
return Group( conn, name )
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs]class Group( common.RestAuthResource ):
"""
An instance of this class represents a group in RestAuth.
*Note:* The constructor *does not* verify that the group actually exists.
This has the advantage of saving one request to the RestAuth service.
If you want to be sure that a user exists, use :py:func:`get` or
:py:func:`get_all`.
:param conn: The connection to the RestAuthServer.
:type conn: :py:class:`.RestAuthConnection`
:param name: The name of this user.
:type name: str
"""
prefix = '/groups/'
def __init__( self, conn, name ):
self.conn = conn
self.name = name
[docs] def get_members( self ):
"""
Get all members of this group.
:return: A list of :py:class:`users <.User>`.
:rtype: list
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group does not exist.
:raise NotAcceptable: When the server cannot generate a response
in the content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
params = {}
resp = self._get( '/%s/users/'%(self.name), params )
if resp.status == http.OK:
# parse user-list:
body = resp.read().decode( 'utf-8' )
names = self.conn.content_handler.unmarshal_list( body )
users = [ restauth_user.User( self.conn, name ) for name in names ]
return users
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def add_user( self, user ):
"""
Add a user to this group.
:param user: The user or the name of the user to add.
:type user: :py:class:`.User` or str
:raise BadRequest: If the server was unable to parse the request
body.
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group or user does not exist.
:raise UnsupportedMediaType: The server does not support the
content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
if user.__class__ == restauth_user.User:
user = user.name
params = { 'user': user }
resp = self._post( '/%s/users/'%(self.name), params )
if resp.status == http.NO_CONTENT:
return
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def add_group( self, group ):
"""
Add a group to this group.
:param group: The group or the name of the group to add.
:type group: :py:class:`.Group` or str
:raise BadRequest: If the server was unable to parse the request
body.
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group or user does not exist.
:raise UnsupportedMediaType: The server does not support the
content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
if group.__class__ == Group:
group = group.name
params = { 'group': group }
path = '/%s/groups/'%(self.name)
resp = self._post( path, params )
if resp.status == http.NO_CONTENT:
return
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def get_groups( self ):
"""
Get a list of sub-groups of this group.
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the sub- or meta-group not exist.
:raise NotAcceptable: When the server cannot generate a response
in the content type used by this connection (see also:
:py:meth:`~.RestAuthConnection.set_content_handler`).
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
path = '/%s/groups/'%(self.name)
resp = self._get( path )
if resp.status == http.OK:
body = resp.read().decode( 'utf-8' )
names = self.conn.content_handler.unmarshal_list( body )
return [ Group( self.conn, name ) for name in names ]
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def remove_group( self, group ):
"""
Remove a sub-group from this group.
:param group: The group or the name of the group to remmove.
:type group: :py:class:`.Group` or str
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the sub- or meta-group not exist.
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
if group.__class__ == Group:
group = group.name
path = '/%s/groups/%s/'%(self.name, group)
resp = self._delete( path )
if resp.status == http.NO_CONTENT:
return
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def remove( self ):
"""
Delete this group.
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group does not exist.
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
resp = self._delete( self.name )
if resp.status == http.NO_CONTENT:
return
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def is_member( self, user ):
"""
Check if the named user is a member.
:param user: The user or the name of a user in question.
:type user: :py:class:`.User` or str
:return: True if the user is a member, False if not.
:rtype: bool
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group or user does not exist.
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
if user.__class__ == restauth_user.User:
user = user.name
path = '/%s/users/%s/'%(self.name, user)
resp = self._get( path )
if resp.status == http.NO_CONTENT:
return True
elif resp.status == http.NOT_FOUND:
if resp.getheader( 'Resource-Type' ) == 'user':
return False
else:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
[docs] def remove_user( self, user ):
"""
Remove the given user from the group.
:raise Unauthorized: When the connection uses wrong credentials.
:raise ResourceNotFound: If the group or user does not exist.
:raise InternalServerError: When the RestAuth service returns HTTP status code 500
:raise UnknownStatus: If the response status is unknown.
"""
if user.__class__ == restauth_user.User:
user = user.name
path = '/%s/users/%s/'%(self.name, user)
resp = self._delete( path )
if resp.status == http.NO_CONTENT:
return
elif resp.status == http.NOT_FOUND:
raise error.ResourceNotFound( resp )
else:
raise UnknownStatus( resp )
def __eq__( self, other ):
"""
Two instances of the class User evaluate as equal if their name
and connection evaluate as equal.
"""
return self.name == other.name and self.conn == other.conn
def __repr__( self ):
import sys
if sys.version_info < (3, 0) and self.name.__class__ == unicode:
return '<Group: {0}>'.format(self.name.encode( 'utf-8' ))
else:
return '<Group: {0}>'.format(self.name)