Ejemplo n.º 1
0
 def __init__(self):
     self.logger = logger("")
     begin()
     self.settings = Settings(self)
     self.worlds = {}
     self.default_world = None
     self.level = Level.engine
     self.logger.info("CocoMUD engine started")
Ejemplo n.º 2
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"
Ejemplo n.º 3
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"
Ejemplo n.º 4
0
def executeSearch(
    user,
    constraints,
    from_,
    to,
    orderBy=None,
    selectRelated=defaultSelectRelated,
    defer=defaultDefer,
):
    """
  Executes a search database query, returning an evaluated QuerySet.
  'user' is the requestor, and should be an authenticated StoreUser
  object or AnonymousUser.  'from_' and 'to' are range bounds, and
  must be supplied.  'constraints', 'orderBy', 'selectRelated', and
  'defer' are as in formulateQuery above.
  """
    tid = uuid.uuid1()
    try:
        _modifyActiveCount(1)
        qs = formulateQuery(constraints,
                            orderBy=orderBy,
                            selectRelated=selectRelated,
                            defer=defer)
        log.begin(
            tid, "search/results", "-", user.username, user.pid,
            user.group.groupname, user.group.pid, str(orderBy), str(from_),
            str(to),
            *reduce(operator.__concat__,
                    [[k, unicode(v)] for k, v in constraints.items()]))
        qs = qs[from_:to]
        c = len(qs)
    except Exception, e:
        # MySQL's FULLTEXT engine chokes on a too-frequently-occurring
        # word (call it a "bad" word) that is not on its own stopword
        # list.  We weed out bad words using our own stopword list, but
        # not if they're quoted, and unfortunately MySQL chokes on bad
        # words quoted or not.  Furthermore, we are unable to add to
        # MySQL's stopword list.  If MySQL chokes, we retry the query
        # without any quotes in the hopes that any quoted bad words will
        # be removed by our own processing.
        if _isMysqlFulltextError(e) and any('"' in constraints.get(f, "")
                                            for f in _fulltextFields):
            constraints2 = constraints.copy()
            for f in _fulltextFields:
                if f in constraints2:
                    constraints2[f] = constraints2[f].replace('"', " ")
            log.success(tid, "-1")
            return executeSearch(user, constraints2, from_, to, orderBy,
                                 selectRelated, defer)
        else:
            log.error(tid, e)
            raise
Ejemplo n.º 5
0
    def __init__(self, config_dir="."):
        self.logger = logger("")
        begin()
        self.config_dir = config_dir
        if config_dir != ".":
            self.logger.info(f"Using an alternative config directory: {config_dir}")

        self.settings = Settings(self, config_dir)
        self.sounds = True
        self.worlds = {}
        self.default_world = None
        self.level = Level.engine
        self.logger.info("CocoMUD engine started")
Ejemplo n.º 6
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"
Ejemplo n.º 7
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)
Ejemplo n.º 8
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)
Ejemplo n.º 9
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)