Пример #1
0
    def search_groups(self, **kwargs):
        """
        Returns a list of resttools.GroupReference objects matching the
        passed parameters. Valid parameters are:
            name: parts_of_name
                name may include the wild-card (*) character.
            stem: group_stem
            member: member netid
            owner: admin netid
            instructor: instructor netid
                stem="course" will be set when this parameter is passed.
            student: student netid
                stem="course" will be set when this parameter is passed.
            affiliate: affiliate_name
            type: search_type
                Values are 'direct' to search for direct membership and
                'effective' to search for effective memberships. Default is
                direct membership.
            scope: search_scope
                Values are 'one' to limit results to one level of stem name
                and 'all' to return all groups.
        """
        kwargs = dict((k.lower(), v.lower()) for k, v in kwargs.iteritems())
        if 'type' in kwargs and (kwargs['type'] != 'direct' and
                                 kwargs['type'] != 'effective'):
            del(kwargs['type'])

        if 'scope' in kwargs and (kwargs['scope'] != 'one' and
                                  kwargs['scope'] != 'all'):
            del(kwargs['scope'])

        if "instructor" in kwargs or "student" in kwargs:
            kwargs["stem"] = "course"

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/search?" + urlencode(kwargs)
        response = dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        groups = []
        root = etree.fromstring(response.data)
        e_grs_list = root.find('groupreferences').findall('groupreference')
        for e in e_grs_list:
            group = GroupReference()
            group.uwregid = e.find('regid').text
            group.title = e.find('title').text
            group.description = e.find('description').text
            group.name = e.find('name').text
            groups.append(group)

        return groups
Пример #2
0
    def delete_group(self, group_id):
        """
        Deletes the group identified by the passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s" % group_id
        response = dao.deleteURL(url, self._headers({}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return True
Пример #3
0
 def __init__(self, conf, actas=None):
     self._service_name = 'gws'
     self._conf = conf
     self._j2env = Environment(loader=PackageLoader('resttools',
                                                    'templates/gws'))
     self._actas = actas
     self.dao = GWS_DAO(conf)
Пример #4
0
    def get_group_by_id(self, group_id):
        """
        Returns a resttools.Group object for the group identified by the
        passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s" % group_id
        response = dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._group_from_xml(response.data)
Пример #5
0
    def create_group(self, group):
        """
        Creates a group from the passed resttools.Group object.
        """
        body = self._xml_from_group(group)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s" % group.name
        response = dao.putURL(url,
                              self._headers({"Accept": "text/xml",
                                             "Content-Type": "text/xml"}),
                              body)

        if response.status != 201:
            raise DataFailureException(url, response.status, response.data)

        return self._group_from_xml(response.data)
Пример #6
0
    def get_effective_member_count(self, group_id):
        """
        Returns a count of effective members for the group identified by the
        passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s/effective_member?view=count" % group_id
        response = dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        root = etree.fromstring(response.data)
        count = root.find('member_count').get("count")

        return int(count)
Пример #7
0
    def is_effective_member(self, group_id, netid):
        """
        Returns True if the netid is in the group, False otherwise.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        # GWS doesn't accept EPPNs on effective member checks, for UW users
        netid = re.sub('@washington.edu', '', netid)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s/effective_member/%s" % (group_id, netid)
        response = dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status == 404:
            return False
        elif response.status == 200:
            return True
        else:
            raise DataFailureException(url, response.status, response.data)
Пример #8
0
    def put_members(self, group_id, members):
        """
        Puts the membership of the group represented by the passed group id.
        Returns a list of members not found.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        body = self._xml_from_members(group_id, members)

        dao = GWS_DAO(self._conf)
        url = "/group_sws/v2/group/%s/member" % group_id
        response = dao.putURL(url,
                              self._headers({"Content-Type": "text/xml",
                                             "If-Match": "*"}),
                              body)

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._notfoundmembers_from_xml(response.data)
Пример #9
0
class GWS(object):
    """
    The GWS object has methods for getting group information.
    """
    def __init__(self, conf, actas=None):
        self._service_name = 'gws'
        self._conf = conf
        self._j2env = Environment(loader=PackageLoader('resttools',
                                                       'templates/gws'))
        self._actas = actas
        self.dao = GWS_DAO(conf)

    QTRS = {'win': 'winter', 'spr': 'spring', 'sum': 'summer', 'aut': 'autumn'}

    def search_groups(self, **kwargs):
        """
        Returns a list of resttools.GroupReference objects matching the
        passed parameters. Valid parameters are:
            name: parts_of_name
                name may include the wild-card (*) character.
            stem: group_stem
            member: member netid
            owner: admin netid
            instructor: instructor netid
                stem="course" will be set when this parameter is passed.
            student: student netid
                stem="course" will be set when this parameter is passed.
            affiliate: affiliate_name
            type: search_type
                Values are 'direct' to search for direct membership and
                'effective' to search for effective memberships. Default is
                direct membership.
            scope: search_scope
                Values are 'one' to limit results to one level of stem name
                and 'all' to return all groups.
        """
        kwargs = dict((k.lower(), v.lower()) for k, v in kwargs.items())
        if 'type' in kwargs and (kwargs['type'] != 'direct' and
                                 kwargs['type'] != 'effective'):
            del(kwargs['type'])

        if 'scope' in kwargs and (kwargs['scope'] != 'one' and
                                  kwargs['scope'] != 'all'):
            del(kwargs['scope'])

        if "instructor" in kwargs or "student" in kwargs:
            kwargs["stem"] = "course"

        url = "/group_sws/v2/search?" + urlencode(kwargs)
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        groups = []
        root = etree.fromstring(response.data)
        e_grs_list = root.find('groupreferences').findall('groupreference')
        for e in e_grs_list:
            group = GroupReference()
            group.uwregid = e.find('regid').text
            group.title = e.find('title').text
            group.description = e.find('description').text
            group.name = e.find('name').text
            groups.append(group)

        return groups

    def get_group_by_id(self, group_id):
        """
        Returns a resttools.Group object for the group identified by the
        passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        url = "/group_sws/v2/group/%s" % group_id
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._group_from_xml(response.data)

    def create_group(self, group):
        """
        Creates a group from the passed resttools.Group object.
        """
        body = self._xml_from_group(group)

        url = "/group_sws/v2/group/%s" % group.name
        response = self.dao.putURL(
            url, self._headers({"Accept": "text/xml", "Content-Type": "text/xml"}),
            body)

        if response.status != 201:
            raise DataFailureException(url, response.status, response.data)

        return self._group_from_xml(response.data)

    def put_group(self, group):
        """
        Updates a group from the passed resttools.Group object.
        """
        body = self._xml_from_group(group)

        url = "/group_sws/v2/group/%s" % group.name
        response = self.dao.putURL(
            url, self._headers({"Accept": "text/xml",
                                "Content-Type": "text/xml", "If-Match": "*"}),
            body)

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._group_from_xml(response.data)

    def delete_group(self, group_id):
        """
        Deletes the group identified by the passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        url = "/group_sws/v2/group/%s" % group_id
        response = self.dao.deleteURL(url, self._headers({}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return True

    def get_members(self, group_id):
        """
        Returns a list of resttools.GroupMember objects for the group
        identified by the passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        url = "/group_sws/v2/group/%s/member" % group_id
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._members_from_xml(response.data)

    def put_membership(self, group_id, members):
        """
        Puts the membership of the group represented by the passed group id.
        Members is an object: name=<member_id>, type=<member_type>
        Returns a list of members not found.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        body = self._xml_from_members(group_id, members)

        url = "/group_sws/v2/group/%s/member" % group_id
        response = self.dao.putURL(
            url, self._headers({"Content-Type": "text/xml", "If-Match": "*"}),
            body)

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._notfoundmembers_from_xml(response.data)

    def put_members(self, group_id, members):
        """
        Puts members into the group represented by the passed group id.
        Members is a list of string. (could be prefaced, e.g. 'u:user_id')
        Returns a list of members not found.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        if len(members) == 0:
            return []

        url = "/group_sws/v2/group/%s/member/%s" % (group_id, ','.join(members))
        response = self.dao.putURL(
            url, self._headers({"Content-Type": "text/xml", "If-Match": "*"}),
            None)

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._notfoundmembers_from_xml(response.data)

    def delete_members(self, group_id, members):
        """
        Delete members from the group represented by the passed group id.
        Members is a list of string.
        Returns a list of members not found.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        if len(members) == 0:
            return True

        url = "/group_sws/v2/group/%s/member/%s" % (group_id, ','.join(members))
        response = self.dao.deleteURL(url, self._headers(
            {"Content-Type": "text/xml", "If-Match": "*"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return True

    def get_effective_members(self, group_id):
        """
        Returns a list of effective resttools.GroupMember objects for the
        group identified by the passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        url = "/group_sws/v2/group/%s/effective_member" % group_id
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        return self._members_from_xml(response.data)

    def get_effective_member_count(self, group_id):
        """
        Returns a count of effective members for the group identified by the
        passed group ID.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        url = "/group_sws/v2/group/%s/effective_member?view=count" % group_id
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status != 200:
            raise DataFailureException(url, response.status, response.data)

        root = etree.fromstring(response.data)
        count = root.find('member_count').get("count")

        return int(count)

    def is_effective_member(self, group_id, netid):
        """
        Returns True if the netid is in the group, False otherwise.
        """
        if not self._is_valid_group_id(group_id):
            raise InvalidGroupID(group_id)

        # GWS doesn't accept EPPNs on effective member checks, for UW users
        netid = re.sub('@washington.edu', '', netid)

        url = "/group_sws/v2/group/%s/effective_member/%s" % (group_id, netid)
        response = self.dao.getURL(url, self._headers({"Accept": "text/xml"}))

        if response.status == 404:
            return False
        elif response.status == 200:
            return True
        else:
            raise DataFailureException(url, response.status, response.data)

    def _group_from_xml(self, data):
        root = etree.fromstring(data)
        gr = root.find('group')
        group_id = gr.find('name').text
        if re.match(r'^course_', group_id):
            group = CourseGroup()
            group.curriculum_abbr = gr.find('course_curr').text.upper()
            group.course_number = gr.find('course_no').text
            group.year = gr.find('course_year').text
            group.quarter = self.QTRS[gr.find('course_qtr').text]
            group.section_id = gr.find('course_sect').text.upper()
            group.sln = gr.find('course_sln').text

            group.instructors = []
            instructors = (gr.find('course_instructors').findall('course_instructor'))
            for instructor in instructors:
                group.instructors.append(GroupMember(name=instructor.text,
                                                     member_type="uwnetid"))
        else:
            group = Group()

        group.name = group_id
        group.uwregid = gr.find('regid').text
        group.title = gr.find('title').text
        group.description = gr.find('description').text
        group.contact = gr.find('contact').text
        group.authnfactor = gr.find('authnfactor').text
        group.classification = gr.find('classification').text
        group.emailenabled = gr.find('emailenabled').text
        group.dependson = gr.find('dependson').text
        group.publishemail = gr.find('publishemail').text

        try:
            group.reporttoorig = gr.find('reporttoorig').text
        except AttributeError:
            # Legacy class name for this attribute
            group.reporttoorig = gr.find('reporttoowner').text

        for user in gr.find('admins').findall('admin'):
            group.admins.append(GroupUser(user.text,
                                          user_type=user.get('type')))

        for user in gr.find('updaters').findall('updater'):
            group.updaters.append(GroupUser(name=user.text,
                                            user_type=user.get("type")))

        for user in gr.find('creators').findall('creator'):
            group.creators.append(GroupUser(name=user.text,
                                            user_type=user.get("type")))

        for user in gr.find('readers').findall('reader'):
            group.readers.append(GroupUser(name=user.text,
                                           user_type=user.get("type")))

        for user in gr.find('optins').findall('optin'):
            group.optins.append(GroupUser(name=user.text,
                                          user_type=user.get("type")))

        for user in gr.find('optouts').findall('optout'):
            group.optouts.append(GroupUser(name=user.text,
                                           user_type=user.get("type")))

        # viewers are not used according to Jim Fox
        # group.viewers = []
        # for user in gr.find('viewers').findall('viewer'):
        #     group.viewers.append(GroupUser(name=user.text,
        #                                    user_type=user.get("type")))
        return group

    def _xml_from_group(self, group):
        template = self._j2env.get_template("group.xml")
        return template.render({"group": group})

    def _members_from_xml(self, data):
        e_mbrs = etree.fromstring(data).find('group').find('members')
        e_mbr_list = e_mbrs.findall('member')

        members = []
        for member in e_mbr_list:
            members.append(GroupMember(name=member.text,
                                       member_type=member.get("type")))

        return members

    def _notfoundmembers_from_xml(self, data):
        members = []
        root = etree.fromstring(data)
        e_nf = root.find('notfoundmembers')
        if e_nf is None:
            return members
        e_nf_list = e_nf.findall('notfoundmember')
        for m in e_nf_list:
            members.append(m.text)

        return members

    def _xml_from_members(self, group_id, members):
        template = self._j2env.get_template("members.xml")
        return template.render({"group_id": group_id, "members": members})

    def _is_valid_group_id(self, group_id):
        if not re.match(r'^[a-z0-9][\w\.-]+$', group_id, re.I):
            return False

        return True

    def _headers(self, headers):
        if self._actas:
            headers = self._add_header(headers, "X-UW-Act-as", self._actas)

        return headers

    def _add_header(self, headers, header, value):
        if not headers:
            return {header: value}

        headers[header] = value
        return headers