Exemple #1
0
    def test_short_client_token_lookup_delegated_patron_identifier_success(self):
        """Test that one library can perform an authdata lookup on a short
        client token generated by a different library.
        """
        # Here's a library that's not a Vendor ID server, but which can
        # generate a Short Client Token for one of its patrons.
        sct_library = self.short_client_token_library
        utility = AuthdataUtility.from_config(sct_library)
        vendor_id, short_client_token = utility.encode_short_client_token(
            "Foreign patron"
        )

        # Here's an AuthdataUtility for the library that _is_
        # a Vendor ID server.
        vendor_id_utility = AuthdataUtility.from_config(self.vendor_id_library)

        # The Vendor ID library knows the secret it shares with the
        # other library -- initialize_adobe() took care of that.
        sct_library_url = sct_library.setting(Configuration.WEBSITE_URL).value
        eq_("%s token secret" % sct_library.short_name,
            vendor_id_utility.secrets_by_library_uri[sct_library_url]
        )
        
        # Because the Vendor ID library shares the Short Client Token
        # library's secret, it can decode a short client token issued
        # by that library, and issue an Adobe ID (UUID).
        token, signature = short_client_token.rsplit("|", 1)
        uuid, label = self.model.short_client_token_lookup(
            token, signature
        )
        
        # The UUID corresponds to a DelegatedPatronIdentifier,
        # associated with the foreign library and the patron
        # identifier that library encoded in its JWT.
        [dpi] = self._db.query(DelegatedPatronIdentifier).filter(
            DelegatedPatronIdentifier.library_uri==sct_library_url).filter(
                DelegatedPatronIdentifier.patron_identifier=="Foreign patron"
            ).all()
        eq_(uuid, dpi.delegated_identifier)
        eq_("Delegated account ID %s" % uuid, label)

        # We get the same UUID and label by passing the token and
        # signature to standard_lookup as username and password.
        # (That's because standard_lookup calls short_client_token_lookup
        # behind the scenes.)
        credentials = dict(username=token, password=signature)
        new_uuid, new_label = self.model.standard_lookup(credentials)
        eq_(new_uuid, uuid)
        eq_(new_label, label)
Exemple #2
0
    def test_authdata_lookup_delegated_patron_identifier_success(self):
        """Test that one library can perform an authdata lookup on a JWT
        generated by a different library.
        """
        # Here's a library that's not a Vendor ID server, but which
        # can generate a JWT for one of its patrons.
        sct_library = self.short_client_token_library
        utility = AuthdataUtility.from_config(sct_library)
        vendor_id, jwt = utility.encode("Foreign patron")

        # Here's an AuthdataUtility for the library that _is_
        # a Vendor ID server.
        vendor_id_utility = AuthdataUtility.from_config(self.vendor_id_library)

        # The Vendor ID library knows the secret it shares with the
        # other library -- initialize_adobe() took care of that.
        sct_library_uri = sct_library.setting(Configuration.WEBSITE_URL).value
        eq_("%s token secret" % sct_library.short_name,
            vendor_id_utility.secrets_by_library_uri[sct_library_uri]
        )

        # Because this library shares the other library's secret,
        # it can decode a JWT issued by the other library, and
        # issue an Adobe ID (UUID).
        uuid, label = self.model.authdata_lookup(jwt)

        # We get the same result if we smuggle the JWT into
        # a username/password lookup as the username.
        uuid2, label2 = self.model.standard_lookup(dict(username=jwt))
        eq_(uuid2, uuid)
        eq_(label2, label)
            
        # The UUID corresponds to a DelegatedPatronIdentifier,
        # associated with the foreign library and the patron
        # identifier that library encoded in its JWT.
        [dpi] = self._db.query(DelegatedPatronIdentifier).filter(
            DelegatedPatronIdentifier.library_uri==sct_library_uri).filter(
                DelegatedPatronIdentifier.patron_identifier=="Foreign patron"
            ).all()
        eq_(uuid, dpi.delegated_identifier)
        eq_("Delegated account ID %s" % uuid, label)
Exemple #3
0
    def test_adobe_id_tags_when_vendor_id_configured(self):
        """When vendor ID delegation is configured, adobe_id_tags()
        returns a list containing a single tag. The tag contains
        the information necessary to get an Adobe ID and a link to the local
        DRM Device Management Protocol endpoint.
        """
        self.initialize_adobe(self._default_library)
        patron_identifier = "patron identifier"
        [element] = self.annotator.adobe_id_tags(patron_identifier)
        eq_('{http://librarysimplified.org/terms/drm}licensor', element.tag)

        key = '{http://librarysimplified.org/terms/drm}vendor'
        eq_(self.adobe_vendor_id.username, element.attrib[key])

        [token, device_management_link] = element.getchildren()

        eq_('{http://librarysimplified.org/terms/drm}clientToken', token.tag)
        # token.text is a token which we can decode, since we know
        # the secret.
        token = token.text
        authdata = AuthdataUtility.from_config(self._default_library)
        decoded = authdata.decode_short_client_token(token)
        expected_url = ConfigurationSetting.for_library(
            Configuration.WEBSITE_URL, self._default_library).value
        eq_((expected_url, patron_identifier), decoded)

        eq_("link", device_management_link.tag)
        eq_("http://librarysimplified.org/terms/drm/rel/devices",
            device_management_link.attrib['rel'])
        expect_url = self.annotator.url_for(
            'adobe_drm_devices',
            library_short_name=self._default_library.short_name,
            _external=True)
        eq_(expect_url, device_management_link.attrib['href'])

        # If we call adobe_id_tags again we'll get a distinct tag
        # object that renders to the same XML.
        [same_tag] = self.annotator.adobe_id_tags(patron_identifier)
        assert same_tag is not element
        eq_(etree.tostring(element), etree.tostring(same_tag))
Exemple #4
0
    def test_from_config(self):
        library = self._default_library
        library2 = self._library()
        self.initialize_adobe(library, [library2])
        library_url = library.setting(Configuration.WEBSITE_URL).value
        library2_url = library2.setting(Configuration.WEBSITE_URL).value
        
        utility = AuthdataUtility.from_config(library)

        registry = ExternalIntegration.lookup(
            self._db, ExternalIntegration.OPDS_REGISTRATION,
            ExternalIntegration.DISCOVERY_GOAL, library=library
        )
        eq_(library.short_name + "token",
            ConfigurationSetting.for_library_and_externalintegration(
                self._db, ExternalIntegration.USERNAME, library, registry).value)
        eq_(library.short_name + " token secret",
            ConfigurationSetting.for_library_and_externalintegration(
                self._db, ExternalIntegration.PASSWORD, library, registry).value)

        eq_(self.TEST_VENDOR_ID, utility.vendor_id)
        eq_(library_url, utility.library_uri)
        eq_(
            {library2_url : "%s token secret" % library2.short_name,
             library_url : "%s token secret" % library.short_name},
            utility.secrets_by_library_uri
        )

        eq_(
            {"%sTOKEN" % library.short_name.upper() : library_url,
             "%sTOKEN" % library2.short_name.upper() : library2_url },
            utility.library_uris_by_short_name
        )

        # If an integration is set up but incomplete, from_config
        # raises CannotLoadConfiguration.
        setting = ConfigurationSetting.for_library_and_externalintegration(
            self._db, ExternalIntegration.USERNAME, library, registry)
        old_short_name = setting.value
        setting.value = None
        assert_raises(
            CannotLoadConfiguration, AuthdataUtility.from_config,
            library
        )
        setting.value = old_short_name

        setting = library.setting(Configuration.WEBSITE_URL)
        old_value = setting.value
        setting.value = None
        assert_raises(
            CannotLoadConfiguration, AuthdataUtility.from_config, library
        )
        setting.value = old_value

        setting = ConfigurationSetting.for_library_and_externalintegration(
            self._db, ExternalIntegration.PASSWORD, library, registry)
        old_secret = setting.value
        setting.value = None
        assert_raises(
            CannotLoadConfiguration, AuthdataUtility.from_config, library
        )
        setting.value = old_secret

        # If other libraries are not configured, that's fine. We'll
        # only have a configuration for ourselves.
        self.adobe_vendor_id.set_setting(
            AuthdataUtility.OTHER_LIBRARIES_KEY, None
        )
        authdata = AuthdataUtility.from_config(library)
        eq_({library_url : "%s token secret" % library.short_name},
            authdata.secrets_by_library_uri)
        eq_({"%sTOKEN" % library.short_name.upper(): library_url},
            authdata.library_uris_by_short_name)

        # Short library names are case-insensitive. If the
        # configuration has the same library short name twice, you
        # can't create an AuthdataUtility.
        self.adobe_vendor_id.set_setting(
            AuthdataUtility.OTHER_LIBRARIES_KEY,
            json.dumps({
                "http://a/" : ("a", "secret1"),
                "http://b/" : ("A", "secret2"),
            })
        )
        assert_raises(ValueError, AuthdataUtility.from_config, library)

        # If there is no Adobe Vendor ID integration set up,
        # from_config() returns None.
        self._db.delete(registry)
        eq_(None, AuthdataUtility.from_config(library))
Adobe ID. This makes sure that they don't suddenly change Adobe IDs
when they start using a client that employs the new JWT-based authdata
system.
"""

import os
import sys
from pdb import set_trace
bin_dir = os.path.split(__file__)[0]
package_dir = os.path.join(bin_dir, "..")
sys.path.append(os.path.abspath(package_dir))
from core.model import (production_session, Patron)
from api.adobe_vendor_id import AuthdataUtility

_db = production_session()
authdata = AuthdataUtility.from_config()
if not authdata:
    print "Adobe IDs not configured, doing nothing."

count = 0
qu = _db.query(Patron)
print "Processing %d patrons." % qu.count()
for patron in qu:
    credential, delegated_identifier = authdata.migrate_adobe_id(patron)
    count += 1
    if not (count % 100):
        print count
        _db.commit()
    if credential is None or delegated_identifier is None:
        # This patron did not have an Adobe ID in the first place.
        # Do nothing.