auth_obj.py :  » Development » SnapLogic » snaplogic » snapi_base » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » SnapLogic 
SnapLogic » snaplogic » snapi_base » auth_obj.py
# $SnapHashLicense:
# 
# SnapLogic - Open source data services
# 
# Copyright (C) 2009, SnapLogic, Inc.  All rights reserved.
# 
# See http://www.snaplogic.org for more information about
# the SnapLogic project. 
# 
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
# 
# "SnapLogic" is a trademark of SnapLogic, Inc.
# 
# 
# $

# $Id: auth_obj.py 6844 2009-03-18 01:05:10Z jbrendel $

import copy

from snaplogic import snapi_base
from snaplogic.common import snap_crypt
from snaplogic.common.snap_exceptions import SnapAuthObjError


class __AuthObj(object):
    """
    A generic object from which all other Auth objects are derived.

    Some common functionality is contained in here.

    An AuthObject can perform further interactions with the server and
    will do so using the credentials that were used by the SNAPI client
    when the object was created.

    AuthObjects are first created in memory, usually by some SNAPI helper
    functions. The genid naturally is not set at that point, which in
    the code here we use as indicator for the object not yet existing
    on the server.

    """
    _name         = None
    _genid        = None
    _description  = None
    _uri          = None
    _credentials  = None


    def __init__(self, name, description="", genid=None, uri=None, credentials=None):
        """
        Create a new auth object.

        @param name:            The name of the new object, for example the
                                group name, user name or ACL name.
        @type  name:            string

        @param description:     An optional description string for the object.
        @type  description:     string

        @param genid:           The generation ID of the object. When the object is
                                first created on the client side then Gen-ID should
                                be specified as None (this signals that the object
                                has not been saved to the server yet). The GenID will
                                be set once the object has been saved. However, if this
                                client-side representation of the object is created
                                based on an already existing object on the server, then
                                the genid value will be specified accordingly, here.
        @type  genid:           string

        @param uri:             The URI of the object on the server. This is None when
                                the object is first created on the client side and is
                                not yet present on the server. In that case it will
                                be set (along with the genid) once the object is saved
                                to the server. However, if this client-side representation
                                of the object is created based on an already existing
                                object on the server, then the uri value will be specified
                                accordingly, here.
        @type  uri:             string

        @param credentials:     A (username, password) tuple.
        @type  credentials:     tuple

        """
        self._name = name

        if description is None:
            description = ""

        self._description = description
        if genid is not None:
            self._genid   = genid
        else:
            self._genid   = None
        self._uri         = uri
        self._credentials = credentials


    def _export_dict(self):
        """
        Return many of the objects items in a dictionary.

        The derived classes may add more stuff to this dictionary.

        """
        d = dict(name        = self._name,
                 description = self._description,
                 genid       = self._genid)
        return d


    def get_name(self):
        """
        Return this object's name.

        @return:    Name
        @rtype:     string

        """
        return self._name


    def set_name(self, name):
        """
        Sets the name of this object.


        Note that this can only be done when the object has not
        yet been saved to the server. Once it has been saved,
        calling this function results in an exception.

        The reason for this is that the name of the object is
        always reflected in the URI of the object on the server
        as well. Renaming the object would therefore effectively
        be a move operation on the server, and we currently do
        not support this.

        @param name:        New name of the object.
        @type  name:        string

        """
        if self._genid is not None:
            raise SnapAuthObjError("Object '%s' is already stored on the server and therefore cannot be renamed." % self._name)

        self._name = name


    def get_description(self):
        """
        Return this object's description.

        @return:    Description
        @rtype:     string

        """
        return self._description


    def set_description(self, description):
        """
        Set the object's description

        @param description:     A new description string.
        @type  description:     string

        """
        self._description = description


    def get_uri(self):
        """
        Return the URI of this object.

        Note: There is no 'set_uri()', since the URI is always a
        function of the name of the object. See the documentation
        for 'set_name()' for more information.

        @return:        The URI of the object on the server.
        @rtype:         string

        """
        return self._uri


    def get_credentials(self):
        """
        Return the credentials with which this object communicates to the server.

        @return:        Credentials that this object uses to communicate
                        with the server.
        @rtype:         tuple (username, password)

        """
        return self._credentials


    def set_credentials(self, creds):
        """
        Change the credentials that this object uses to communicate to the server.

        This is necessary, for example, if we got a user from the server with the
        user credentials, but now want to delete this server. Deletion is only
        possible with admin credentials.

        @param credentials:        A (username, password) tuple.
        @type: credentials:        tuple

        """
        (username, password) = creds   # Just checking the right format
        self._credentials = creds


    def delete(self):
        """
        Deletes this object on the server.

        The GenID will be set to zero, making it invalid.

        """
        if self._genid is None:
            raise SnapAuthObjError("Object '%s' is not yet stored on the server and therefore cannot be deleted." % self._name)
            
        ret = snapi_base._send_request('DELETE', self._uri, None, None, self._credentials)
        self._genid = "0"
        return ret


    def refresh(self):
        """
        Refresh this object on the server.

        In this super class here we just perform a little test,
        the actual updating of the client-side object needs to
        be implemented by the child classes.

        """
        if self._genid is None:
            raise SnapAuthObjError("Object '%s' is not yet stored on the server and therefore cannot be refreshed." % self._name)


class AuthUser(__AuthObj):

    __password    = None
    __groups      = None
    __email       = None

    __clear_text_password = None

    def __init__(self, username, password=None, description="", email="", genid=None, uri=None, credentials=None):
        """
        Initialize a user object.

        @param username:        The name of the user.
        @type  username:        string

        @param password:        The password of the user in clear text.
                                Is None if the user was created from the server.
        @type  password:        string

        @param description:     A free-text description that can be added
                                for this user. Optional.
        @type  description:     string

        @param email:           The email address of the user. Optional.
        @type  email:           string

        @param genid:           The generational ID of the user. This needs to
                                match with what the server has in memory, or else
                                any updates to the user's definition will fail.
                                It is set automatically when the system creates
                                a user by reading it from the server.
        @type  genid:           int

        @param uri:             The URI of the user on the server.
        @type  uri:             string

        @param credentials:     The credentials used by this object to communicate
                                with the server.
        @type  credentials:     tuple

        """
        super(AuthUser, self).__init__(username, description, genid, uri, credentials)

        if email is None:
            email = ""
        self.__email       = email

        self.__groups      = []

        if password:
            self.set_password(password)


    def __str__(self):
        """
        Return a string representation of the user object.

        """
        return "Username: '%s', Groups: %s, GenID: %s, URI: '%s', Credentials: %s" % (self._name, self.__groups, self._genid, self._uri, self._credentials)


    def _export_dict(self):
        """
        Return the entire user definition as a dict, except the password.

        Password requires some extra handling, which depends on the context
        of the caller. To see examples just look for calls to this function
        to get an idea what the caller does about the password.

        """
        d = super(AuthUser, self)._export_dict()
        d['email'] = self.__email
        return d


    def set_password(self, text):
        """
        Set the password for this user.

        The password needs to be specified as clear text.

        @param  text:       The password text in clear text.
        @type   text:       string

        """
        # When executing 'save()' the password is only written back to the server if it really
        # was changed. This is indicated by __password being something else than None.
        # This is necessary, because the server cannot tell us the clear text password, only
        # an encrypted password, which is not very useful to the user. Thus, the password can
        # only be set, but cannot be read.
        self.__password = snap_crypt.obfuscate(text)

        # We need to store the clear-text version of the password, since this will be part
        # of our credentials AFTER the next update of the object to the server. Because if
        # we change the password in the user then the necessary credential will have changed
        # afterwards as well.
        self.__clear_text_password = text


    def set_email(self, email):
        """
        Set the email address for this user.

        @param email:   Email address of the user.
        @type  email:   string

        """
        self.__email = email


    def get_email(self):
        """
        Return the user's email address.

        @return:    Email address.
        @rtype:     string

        """
        return self.__email


    def get_groups(self):
        """
        Return the groups that the user belongs to.

        The list is a copy of what we have on file in the
        user object.

        @return:        A list of groupnames.
        @rtype:         list

        """
        return copy.copy(self.__groups)


    def save(self):
        """
        Store the potentially modified user to the server.

        """
        d = self._export_dict()
        # Need to see if we can create this thing on the server, if we
        # haven't done so already. This could fail, for example if the specified
        # user exists already.
        if self._genid is None:
            # If the object hasn't been created on the server yet then we know
            # the URI is merely the server's main URI. The GenID is not set yet,
            # and the create REST interface doesn't want to see the genid anyway,
            # which is why we remove it before the call.
            del d['genid']
            d['password'] = self.__clear_text_password
            ret = snapi_base.auth_user_entry_create(self._uri, d, self._credentials)
            # We finally learn our true URI on the server as part of the return
            # value. So, from now on the URI we store in the object is not just
            # the server URI, but the actual object's URI.
            self._uri = self._uri + ret[0]
        else:
            if self.__password:
                d['password']            = self.__password
                d['password_obfuscated'] = 'yes'

            ret = snapi_base.auth_user_entry_edit(self._uri, d, self._credentials)

        # A tuple with three elements is returned, the second one is the new genid
        self._genid = ret[1]

        if self.__password:
            self.__password = None
            # Now we need to update our own credentials, since the password
            # on the server was changed with our last update.
            if self._name == self._credentials[0]:
                self._credentials = (self._name, self.__clear_text_password)
                self.__clear_text_password = None
            else:
                # We don't want to update our credentials, since we are operating
                # with someone else's credentials (probably admin).
                pass


    def refresh(self):
        """
        Refresh this user object with latest information from server.

        The elements in the user's group are not maintained in the user object.
        Thus, if some other operations have updated the group memberships one
        needs to perform this refresh here to get the latest group membership
        of the user.

        @return:            A dictionary that contains all the elements that
                            have been updated with this operation. The OLD
                            values that were in the user object are returned
                            in the dictionary. The object itself will contain
                            all the new values from the server.

                            This allows for the intelligent treatment of any updates
                            and allows post-processing so that differences between
                            the in-memory version and the server version can be
                            dealt with.

                            Note that not all elements may appear in this diff-list.
                            For example, if any changes have been made to the password
                            then 'password' may still not appear in this list, since
                            we cannot compare the password value of the server with
                            the one we have in memory.

        @rtype:             dict

        """
        super(AuthUser, self).refresh()
        ret = snapi_base.auth_user_entry_get(str(self._uri), self._name, self._credentials)

        diff = {}

        if self._genid  != ret['genid']:
            diff['genid'] = self._genid
            self._genid  = ret['genid']

        if self._description  != ret['description']:
            diff['description'] = self._description
            self._description  = ret['description']

        if self.__email  != ret['email']:
            diff['email'] = self.__email
            self.__email  = ret['email']
        
        # Comparison of the groups list is a bit more involved. But we know it's different
        # if the length is different  OR  any element of one list is not contained in the other.
        if len(self.__groups) != len(ret['groups'])  or  [ x for x in self.__groups if x not in ret['groups']]:
            diff['groups']     = self.__groups     # Don't need to make copy, since old list is not needed anymore
            self.__groups      = ret['groups']

        return diff


class AuthGroup(__AuthObj):

    __users       = None

    def __init__(self, groupname, description="", genid=None, uri=None, credentials=None):
        """
        Initialize a group object.

        @param groupname:       The name of the group.
        @type  groupname:       string

        @param description:     A free-text description that can be added
                                for this group. Optional.
        @type  description:     string

        @param genid:           The generational ID of the group. This needs to
                                match with what the server has in memory, or else
                                any updates to the group's definition will fail.
                                It is set automatically when the system creates
                                a group by reading it from the server.
        @type  genid:           string

        @param uri:             The URI of the group on the server.
        @type  uri:             string

        @param credentials:     The credentials used by this object to communicate
                                with the server.
        @type  credentials:     tuple

        """
        super(AuthGroup, self).__init__(groupname, description, genid, uri, credentials)
        self.__users = []


    def __str__(self):
        """
        Return a string representation of the group object.

        """
        return "Groupname: '%s', Users: %s, GenID: %s, URI: '%s', Credentials: %s" % (self._name, self.__users, self._genid, self._uri, self._credentials)


    def _export_dict(self):
        """
        Return the entire group definition as a dict.

        """
        d = super(AuthGroup, self)._export_dict()
        d['users'] = self.__users
        return d


    def get_users(self):
        """
        Return the users that the group contains.

        @return:        A list of usernames.
        @rtype:         list

        """
        return self.__users


    def set_users(self, users):
        """
        Set the entire user list, removing any old user list from the group.

        @param users:  List of users, specified either as names of user objects.
        @type  users:  list

        """
        usernames = []
        for u in users:
            try:
                uname = u.get_name()
            except AttributeError:
                uname = u
            usernames.append(uname)

        self.__users = usernames


    def add_user(self, user):
        """
        Add a single user to the group.

        @param user:        New user to be added, either specified as
                            string or as user object.
        @type  user:        string or L{AuthUser}

        """
        try:
            # Can be specified either as a user object...
            username = user.get_name()
        except AttributeError:
            # ... or just by name
            username = user

        # The user can only appear once in the group
        if username in self.__users:
            raise SnapAuthObjError("User '%s' is already in group." % username)

        # Either way, only the name is stored, of course...
        self.__users.append(username)


    def del_user(self, user):
        """
        Remove a user from the group

        @param user:        User to be removed, either specified as
                            string or as user object.
        @type  user:        string or L{AuthUser}

        """
        try:
            # Can be specified either as a user object...
            username = user.get_name()
        except AttributeError:
            # ... or just by name
            username = user
        try:
            i = self.__users.index(username)
            self.__users.pop(i)
        except ValueError:
            raise SnapAuthObjError("User '%s' is not in group." % username)
            

    def save(self):
        """
        Store the potentially modified group to the server.

        """
        d = self._export_dict()
        # Need to see if we can create this thing on the server, if we
        # haven't done so already. This could fail, for example if the specified
        # user exists already.
        if self._genid is None:
            # If the object hasn't been created on the server yet then we know
            # the URI is merely the server's main URI. The GenID is not set yet,
            # and the create REST interface doesn't want to see the genid anyway,
            # which is why we remove it before the call.
            del d['genid']
            ret = snapi_base.auth_group_entry_create(self._uri, d, self._credentials)
            # We finally learn our true URI on the server as part of the return
            # value. So, from now on the URI we store in the object is not just
            # the server URI, but the actual object's URI.
            self._uri = self._uri + ret[0]
        else:
            ret = snapi_base.auth_group_entry_edit(self._uri, d, self._credentials)

        # A tuple with three elements is returned, the second one is the new genid
        self._genid = ret[1]


    def refresh(self):
        """
        Refresh this group object with latest information from server.

        @return:            A dictionary that contains all the elements that
                            have been updated with this operation. The OLD
                            values that were in the group object are returned
                            in the dictionary. The object itself will contain
                            all the new values from the server.

                            This allows for the intelligent treatment of any updates
                            and allows post-processing so that differences between
                            the in-memory version and the server version can be
                            dealt with.

        @rtype:             dict

        """
        super(AuthGroup, self).refresh()
        ret = snapi_base.auth_group_entry_get(str(self._uri), self._name, self._credentials)

        diff = {}

        if self._genid  != ret['genid']:
            diff['genid'] = self._genid
            self._genid  = ret['genid']

        if self._description  != ret['description']:
            diff['description'] = self._description
            self._description  = ret['description']

        # Comparison of the users list is a bit more involved. But we know it's different
        # if the length is different  OR  any element of one list is not contained in the other.
        if len(self.__users) != len(ret['users'])  or  [ x for x in self.__users if x not in ret['users']]:
            diff['users']     = self.__users     # Don't need to make copy, since old list is not needed anymore
            self.__users      = ret['users']

        return diff


class AuthAcl(__AuthObj):

    __rules       = None

    def __init__(self, aclname, description="", genid=None, uri=None, credentials=None):
        """
        Initialize an ACL object.

        @param aclname:         The aclname of the ACL. This is a path, always starting
                                with a '/'.
        @type  aclname:         string

        @param description:     A free-text description that can be added
                                for this ACL. Optional.
        @type  description:     string

        @param genid:           The generational ID of the ACL. This needs to
                                match with what the server has in memory, or else
                                any updates to the ACL's definition will fail.
                                It is set automatically when the system creates
                                an ACL by reading it from the server.
        @type  genid:           string

        @param uri:             The URI of the ACL on the server.
        @type  uri:             string

        @param credentials:     The credentials used by this object to communicate
                                with the server.
        @type  credentials:     tuple

        """
        super(AuthAcl, self).__init__(aclname, description, genid, uri, credentials)
        self.__rules       = []


    def __str__(self):
        """
        Return a string representation of the ACL object.

        """
        return "Name: '%s', Rules: %s, GenID: %s, URI: '%s', Credentials: %s" % (self._name, self.__rules, self._genid, self._uri, self._credentials)


    def _export_dict(self):
        """
        Return the entire ACL definition as a dict.

        """
        d = super(AuthAcl, self)._export_dict()
        d['rules'] = self.__rules
        return d


    def get_rules(self):
        """
        Return the rules that the ACL contains.

        @return:        A list of rules. For example: [ "deny user foo",
                        "allow group bar permission read write" ]
        @rtype:         list

        """
        return self.__rules


    def set_rules(self, rules):
        """
        Set the entire rules list, removing any old rules from the ACL.

        @param rules:  List of rules, specified as a list of strings.
        @type  rules:  list

        """
        self.__rules = rules


    def add_rule(self, rule):
        """
        Add a single rule to the ACL.

        @param rule:        New rule to be added, specified as a string
        @type  rule:        string

        """
        # Either way, only the name is stored, of course...
        self.__rules.append(rule)


    def save(self):
        """
        Store the potentially modified ACL to the server.

        """
        d = self._export_dict()
        # Need to see if we can create this thing on the server, if we
        # haven't done so already. This could fail, for example if the specified
        # user exists already.
        if self._genid is None:
            # If the object hasn't been created on the server yet then we know
            # the URI is merely the server's main URI. The GenID is not set yet,
            # and the create REST interface doesn't want to see the genid anyway,
            # which is why we remove it before the call.
            del d['genid']
            ret = snapi_base.auth_acl_entry_create(self._uri, d, self._credentials)
            # We finally learn our true URI on the server as part of the return
            # value. So, from now on the URI we store in the object is not just
            # the server URI, but the actual object's URI.
            self._uri = self._uri + ret[0]
        else:
            ret = snapi_base.auth_acl_entry_edit(self._uri, d, self._credentials)

        # A tuple with three elements is returned, the second one is the new genid
        self._genid = ret[1]


    def refresh(self):
        """
        Refresh this ACL object with latest information from server.

        @return:            A dictionary that contains all the elements that
                            have been updated with this operation. The OLD
                            values that were in the ACL object are returned
                            in the dictionary. The object itself will contain
                            all the new values from the server.

                            This allows for the intelligent treatment of any updates
                            and allows post-processing so that differences between
                            the in-memory version and the server version can be
                            dealt with.

        @rtype:             dict

        """
        super(AuthAcl, self).refresh()
        ret = snapi_base.auth_acl_entry_get(str(self._uri), self._name, self._credentials)

        diff = {}
        lower_rules = [ x.lower() for x in ret['rules'] ]

        if self._genid  != ret['genid']:
            diff['genid'] = self._genid
            self._genid  = ret['genid']

        if self._description  != ret['description']:
            diff['description'] = self._description
            self._description  = ret['description']

        # Comparison of the rules list is a bit more involved. But we know it's different
        # if the length is different  OR  any element of one list is not contained in the other.
        if len(self.__rules) != len(ret['rules'])  or  [ x for x in self.__rules if x.lower() not in lower_rules]:
            diff['rules']     = self.__rules     # Don't need to make copy, since old list is not needed anymore
            self.__rules      = ret['rules']

        return diff

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.