Example #1
0
def getMetadata(identifier,
                user=ezidapp.models.AnonymousUser,
                prefixMatch=False):
    """
  Returns all metadata for a given qualified identifier, e.g.,
  "doi:10.5060/FOO".  'user' is the requestor and should be an
  authenticated StoreUser object.  The successful return is a pair
  (status, dictionary) where 'status' is a string that includes the
  canonical, qualified form of the identifier, as in:

    success: doi:10.5060/FOO

  and 'dictionary' contains element (name, value) pairs.  Unsuccessful
  returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded

  If 'prefixMatch' is true, prefix matching is enabled and the
  returned identifier is the longest identifier that matches a
  (possibly proper) prefix of the requested identifier.  In such a
  case, the status string resembles:

    success: doi:10.5060/FOO in_lieu_of doi:10.5060/FOOBAR
  """
    nqidentifier = util.normalizeIdentifier(identifier)
    if nqidentifier == None: return "error: bad request - invalid identifier"
    tid = uuid.uuid1()
    if not _acquireIdentifierLock(nqidentifier, user.username):
        return "error: concurrency limit exceeded"
    try:
        log.begin(tid, "getMetadata", nqidentifier, user.username, user.pid,
                  user.group.groupname, user.group.pid, str(prefixMatch))
        si = ezidapp.models.getIdentifier(nqidentifier, prefixMatch)
        if not policy.authorizeView(user, si):
            log.forbidden(tid)
            return "error: forbidden"
        d = si.toLegacy()
        util2.convertLegacyToExternal(d)
        if si.isDoi: d["_shadowedby"] = si.arkAlias
        log.success(tid)
        if prefixMatch and si.identifier != nqidentifier:
            return ("success: %s in_lieu_of %s" %
                    (si.identifier, nqidentifier), d)
        else:
            return ("success: " + nqidentifier, d)
    except ezidapp.models.StoreIdentifier.DoesNotExist:
        log.badRequest(tid)
        return "error: bad request - no such identifier"
    except Exception, e:
        log.error(tid, e)
        return "error: internal server error"
Example #2
0
def mintIdentifier(shoulder, user, metadata={}):
    """
  Mints an identifier under the given qualified shoulder, e.g.,
  "doi:10.5060/".  'user' is the requestor and should be an
  authenticated StoreUser object.  'metadata' should be a dictionary
  of element (name, value) pairs.  If an initial target URL is not
  supplied, the identifier is given a self-referential target URL.
  The successful return is a string that includes the canonical,
  qualified form of the new identifier, as in:

    success: ark:/95060/fk35717n0h

  For DOI identifiers, the string also includes the qualified shadow
  ARK, as in:

    success: doi:10.5060/FK35717N0H | ark:/b5060/fk35717n0h

  Unsuccessful returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded
  """
    tid = uuid.uuid1()
    try:
        log.begin(tid, "mintIdentifier", shoulder, user.username, user.pid,
                  user.group.groupname, user.group.pid)
        s = ezidapp.models.getExactShoulderMatch(shoulder)
        if s == None:
            log.badRequest(tid)
            return "error: bad request - no such shoulder"
        if s.isUuid:
            identifier = "uuid:" + str(uuid.uuid1())
        else:
            if s.minter == "":
                log.badRequest(tid)
                return "error: bad request - shoulder does not support minting"
            # Minters always return unqualified ARKs.
            ark = noid_nog.getMinter(s.minter).mintIdentifier()
            if s.isArk:
                identifier = "ark:/" + ark
            elif s.isDoi:
                doi = util.shadow2doi(ark)
                assert util.doi2shadow(doi) == ark, "invalid DOI shadow ARK"
                identifier = "doi:" + doi
            else:
                assert False, "unhandled case"
            assert identifier.startswith(s.prefix),\
              "minted identifier does not match shoulder"
    except Exception, e:
        log.error(tid, e)
        return "error: internal server error"
Example #3
0
def deleteIdentifier(identifier, user, updateExternalServices=True):
    """
  Deletes an identifier having the given qualified name, e.g.,
  "doi:10.5060/FOO".  'user' is the requestor and should be an
  authenticated StoreUser object.  The successful return is a string
  that includes the canonical, qualified form of the now-nonexistent
  identifier, as in:

    success: doi:/10.5060/FOO

  Unsuccessful returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded
  """
    nqidentifier = util.normalizeIdentifier(identifier)
    if nqidentifier == None:
        return "error: bad request - invalid identifier"
    tid = uuid.uuid1()
    if not _acquireIdentifierLock(nqidentifier, user.username):
        return "error: concurrency limit exceeded"
    try:
        log.begin(
            tid,
            "deleteIdentifier",
            nqidentifier,
            user.username,
            user.pid,
            user.group.groupname,
            user.group.pid,
        )
        si = ezidapp.models.getIdentifier(nqidentifier)
        if not policy.authorizeDelete(user, si):
            log.forbidden(tid)
            return "error: forbidden"
        if not si.isReserved and not user.isSuperuser:
            log.badRequest(tid)
            return "error: bad request - identifier status does not support deletion"
        with django.db.transaction.atomic():
            si.delete()
            ezidapp.models.update_queue.enqueue(si, "delete",
                                                updateExternalServices)
    except ezidapp.models.StoreIdentifier.DoesNotExist:
        log.badRequest(tid)
        return "error: bad request - no such identifier"
    except Exception, e:
        log.error(tid, e)
        return "error: internal server error"
Example #4
0
def setMetadata(identifier,
                user,
                metadata,
                updateExternalServices=True,
                internalCall=False):
    """
  Sets metadata elements of a given qualified identifier, e.g.,
  "doi:10.5060/FOO".  'user' is the requestor and should be an
  authenticated StoreUser object.  'metadata' should be a dictionary
  of element (name, value) pairs.  If an element being set already
  exists, it is overwritten, if not, it is created; existing elements
  not set are left unchanged.  Of the reserved metadata elements, only
  "_owner", "_target", "_profile", "_status", and "_export" may be set
  (unless the user is the EZID administrator).  The "_crossref"
  element may be set only in certain situations.  The successful
  return is a string that includes the canonical, qualified form of
  the identifier, as in:

    success: doi:10.5060/FOO

  Unsuccessful returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded
  """
    nqidentifier = util.normalizeIdentifier(identifier)
    if nqidentifier == None: return "error: bad request - invalid identifier"
    tid = uuid.uuid1()
    if not internalCall:
        if not _acquireIdentifierLock(nqidentifier, user.username):
            return "error: concurrency limit exceeded"
    try:
        log.begin(tid, "setMetadata", nqidentifier, user.username, user.pid,
                  user.group.groupname, user.group.pid,
                  *[a for p in metadata.items() for a in p])
        si = ezidapp.models.getIdentifier(nqidentifier)
        if not policy.authorizeUpdate(user, si):
            log.forbidden(tid)
            return "error: forbidden"
        previousOwner = si.owner
        si.updateFromUntrustedLegacy(metadata,
                                     allowRestrictedSettings=user.isSuperuser)
        if si.isCrossref and not si.isReserved and updateExternalServices:
            si.crossrefStatus = ezidapp.models.StoreIdentifier.CR_WORKING
            si.crossrefMessage = ""
        if "_updated" not in metadata: si.updateTime = ""
        si.my_full_clean()
        if si.owner != previousOwner:
            if not policy.authorizeOwnershipChange(user, previousOwner,
                                                   si.owner):
                log.badRequest(tid)
                return "error: bad request - ownership change prohibited"
        with django.db.transaction.atomic():
            si.save()
            ezidapp.models.update_queue.enqueue(si, "update",
                                                updateExternalServices)
    except ezidapp.models.StoreIdentifier.DoesNotExist:
        log.badRequest(tid)
        return "error: bad request - no such identifier"
    except django.core.exceptions.ValidationError, e:
        log.badRequest(tid)
        return "error: bad request - " + util.formatValidationError(e)
Example #5
0
def createIdentifier(identifier, user, metadata={}, updateIfExists=False):
    """
  Creates an identifier having the given qualified name, e.g.,
  "doi:10.5060/FOO".  'user' is the requestor and should be an
  authenticated StoreUser object.  'metadata' should be a dictionary
  of element (name, value) pairs.  If an initial target URL is not
  supplied, the identifier is given a self-referential target URL.
  The successful return is a string that includes the canonical,
  qualified form of the new identifier, as in:

    success: ark:/95060/foo

  For DOI identifiers, the string also includes the qualified shadow
  ARK, as in:

    success: doi:10.5060/FOO | ark:/b5060/foo

  Unsuccessful returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded

  If 'updateIfExists' is true, an "identifier already exists" error
  falls through to a 'setMetadata' call.
  """
    nqidentifier = util.normalizeIdentifier(identifier)
    if nqidentifier == None: return "error: bad request - invalid identifier"
    tid = uuid.uuid1()
    if not _acquireIdentifierLock(nqidentifier, user.username):
        return "error: concurrency limit exceeded"
    try:
        log.begin(tid, "createIdentifier", nqidentifier, user.username,
                  user.pid, user.group.groupname, user.group.pid,
                  *[a for p in metadata.items() for a in p])
        if not policy.authorizeCreate(user, nqidentifier):
            log.forbidden(tid)
            return "error: forbidden"
        si = ezidapp.models.StoreIdentifier(
            identifier=nqidentifier,
            owner=(None if user == ezidapp.models.AnonymousUser else user))
        si.updateFromUntrustedLegacy(metadata,
                                     allowRestrictedSettings=user.isSuperuser)
        if si.isDoi:
            s = ezidapp.models.getLongestShoulderMatch(si.identifier)
            # Should never happen.
            assert s != None, "no matching shoulder found"
            if s.isDatacite:
                if si.datacenter == None: si.datacenter = s.datacenter
            elif s.isCrossref:
                if not si.isCrossref:
                    if si.isReserved:
                        si.crossrefStatus = ezidapp.models.StoreIdentifier.CR_RESERVED
                    else:
                        si.crossrefStatus = ezidapp.models.StoreIdentifier.CR_WORKING
            else:
                assert False, "unhandled case"
        si.my_full_clean()
        if si.owner != user:
            if not policy.authorizeOwnershipChange(user, user, si.owner):
                log.badRequest(tid)
                return "error: bad request - ownership change prohibited"
        with django.db.transaction.atomic():
            si.save()
            ezidapp.models.update_queue.enqueue(si, "create")
    except django.core.exceptions.ValidationError, e:
        log.badRequest(tid)
        return "error: bad request - " + util.formatValidationError(e)
Example #6
0
                     si.crossrefStatus = ezidapp.models.StoreIdentifier.CR_WORKING
         else:
             assert False, "unhandled case"
     si.my_full_clean()
     if si.owner != user:
         if not policy.authorizeOwnershipChange(user, user, si.owner):
             log.badRequest(tid)
             return "error: bad request - ownership change prohibited"
     with django.db.transaction.atomic():
         si.save()
         ezidapp.models.update_queue.enqueue(si, "create")
 except django.core.exceptions.ValidationError, e:
     log.badRequest(tid)
     return "error: bad request - " + util.formatValidationError(e)
 except django.db.utils.IntegrityError:
     log.badRequest(tid)
     if updateIfExists:
         return setMetadata(identifier, user, metadata, internalCall=True)
     else:
         return "error: bad request - identifier already exists"
 except Exception, e:
     log.error(tid, e)
     return "error: internal server error"
 else:
     log.success(tid)
     if si.isDoi:
         return "success: %s | %s" % (nqidentifier, si.arkAlias)
     else:
         return "success: " + nqidentifier
 finally:
     _releaseIdentifierLock(nqidentifier, user.username)
Example #7
0
def _mintIdentifier(shoulder, user, metadata={}):
    """
  Mints an identifier under the given qualified shoulder, e.g.,
  "doi:10.5060/".  'user' is the requestor and should be an
  authenticated StoreUser object.  'metadata' should be a dictionary
  of element (name, value) pairs.  If an initial target URL is not
  supplied, the identifier is given a self-referential target URL.
  The successful return is a string that includes the canonical,
  qualified form of the new identifier, as in:

    success: ark:/95060/fk35717n0h

  For DOI identifiers, the string also includes the qualified shadow
  ARK, as in:

    success: doi:10.5060/FK35717N0H | ark:/b5060/fk35717n0h

  Unsuccessful returns include the strings:

    error: forbidden
    error: bad request - subreason...
    error: internal server error
    error: concurrency limit exceeded
  """
    tid = uuid.uuid1()

    # TODO: We want to be able to support rendering error messages to end users in
    # production like current version of EZID does without breaking rendering of
    # Django's exception diagnostics page in debug mode and without
    # having to wrap large sections of code in exception handlers just for redirecting
    # to a logger.

    log.begin(
        tid,
        "mintIdentifier",
        shoulder,
        user.username,
        user.pid,
        user.group.groupname,
        user.group.pid,
    )

    shoulder_model = ezidapp.models.getExactShoulderMatch(shoulder)

    if shoulder_model is None:
        log.badRequest(tid)
        # TODO: Errors should be raised, not returned.
        return "error: bad request - no such shoulder"

    if shoulder_model.isUuid:
        identifier = "uuid:" + str(uuid.uuid1())
    else:
        if shoulder_model.minter == "":
            log.badRequest(tid)
            return "error: bad request - shoulder does not support minting"

        identifier = minter.mint_id(shoulder_model)
        logger.debug('Minter returned identifier: {}'.format(identifier))

        if shoulder_model.prefix.startswith('doi:'):
            identifier = shoulder_model.prefix + identifier.upper()
        elif shoulder_model.prefix.startswith('ark:/'):
            identifier = shoulder_model.prefix + identifier.lower()
        else:
            raise False, 'Expected ARK or DOI prefix, not "{}"'.format(
                shoulder_model.prefix)

        logger.debug('Final shoulder + identifier: {}'.format(identifier))

    log.success(tid, identifier)

    return createIdentifier(identifier, user, metadata)