Пример #1
0
def _validate_formats(data, schema):
    """Go through schema checking the formats date-time, time and base64"""
    if 'format' in schema:
        format_name = schema['format']
        if format_name == "date-time" and not validate_rfc3339(data):
            raise ForbiddenError("A date-time was not in the required format.")
        elif format_name == "time" and not _validate_timestr(data):
            raise ForbiddenError("A time was not in the required format.")
        elif format_name == "base64" and not _validate_base64(data):
            raise ForbiddenError("A string was not valid base64.")
        elif format_name == "timezone" and not _validate_timezone(data):
            raise ForbiddenError("A string was not a valid timezone.")
    if 'properties' in schema and isinstance(schema['properties'], dict):
        for key, value in data.items():
            try:
                _validate_formats(value, schema['properties'][key])
            except (TypeError, KeyError):
                pass
    if 'additionalProperties' in schema:
        if isinstance(schema['additionalProperties'], dict):
            for value in data.values():
                try:
                    _validate_formats(value, schema['additionalProperties'])
                except TypeError:
                    pass
    if 'items' in schema and isinstance(schema['items'], dict):
        for item in data:
            try:
                _validate_formats(item, schema['items'])
            except TypeError:
                pass
Пример #2
0
def add_listener_update(doc, req):
    """
    Update function: ``payload_telemetry/_update/add_listener``

    Given a prototype payload_telemetry JSON document in the request body,
    containing just the _raw telemetry string and one entry in receivers,
    create the document or merge this listener into it as appropriate.

    Used by listeners when a new payload telemetry string has been received.

    Usage::

        PUT /habitat/_design/payload_telemetry/_update/add_listener/<doc ID>

        {
            "data": {
                "_raw": "<base64 raw telemetry data>"
            },
            "receivers": {
                "<receiver callsign>": {
                    "time_created": "<RFC3339 timestamp>",
                    "time_uploaded": "<RFC3339 timestamp>",
                    <other keys as desired, for instance
                     latest_listener_telemetry, latest_listener_info, etc>
                }
            }
        }

    The document ID should be sha256(doc["data"]["_raw"]) in hexadecimal.

    Returns "OK" if everything was fine, otherwise CouchDB will raise an error.
    Errors might occur in validation (in which case the validation error is
    returned) or because of a save conflict. In the event of a save conflict,
    uploaders should retry the same request until the conflict is resolved.
    """
    protodoc = json.loads(req["body"])
    if "data" not in protodoc or "_raw" not in protodoc["data"]:
        raise ForbiddenError("doc.data._raw is required")
    if "receivers" not in protodoc or len(protodoc["receivers"]) != 1:
        raise ForbiddenError("doc.receivers must exist and have one receiver")
    callsign = protodoc["receivers"].keys()[0]
    protodoc["receivers"][callsign]["time_server"] = now_to_rfc3339_utcoffset()
    if not doc:
        doc = {
            "_id": req["id"],
            "type": "payload_telemetry",
            "data": {
                "_raw": protodoc["data"]["_raw"]
            },
            "receivers": {}
        }
    doc["receivers"][callsign] = protodoc["receivers"][callsign]
    return doc, "OK"
Пример #3
0
def _check_only_new(new, old):
    """
    Raise an error if any items in old are not present unchanged in new.
    """
    for k in old:
        if k == u'_rev':
            continue
        if k not in new:
            raise ForbiddenError("You may not remove objects.")
        if isinstance(old[k], dict):
            _check_only_new(new[k], old[k])
        else:
            if new[k] != old[k]:
                raise ForbiddenError("You may not edit existing items.")
Пример #4
0
def _validate_ukhas(sentence):
    """
    For UKHAS sentences, check that the checksum is an allowable type and that
    fields with structural requirements are valid.
    """
    checksums = ["xor", "crc16-ccitt", "fletcher-16", "fletcher-16-256",
                 "none"]
    if 'checksum' in sentence:
        if sentence['checksum'] not in checksums:
            raise ForbiddenError("Invalid checksum algorithm.")
    else:
        raise ForbiddenError("UKHAS sentences must have a checksum.")

    if 'fields' in sentence:
        if len(sentence['fields']) < 1:
            raise ForbiddenError(
                "UKHAS sentences must have at least one field.")

        field_names = []
        for field in sentence['fields']:
            if field['name'][0] == '_':
                raise ForbiddenError("Field names may not start with _")
            if field['name'] == 'payload':
                raise ForbiddenError("Field name may not be 'payload'")
            field_names.append(field['name'])
            if field['sensor'] == "stdtelem.coordinate":
                if 'format' not in field:
                    raise ForbiddenError(
                        "Coordinate fields must have formats.")

        if len(field_names) != len(set(field_names)):
            raise ForbiddenError("Duplicate field names")
    else:
        raise ForbiddenError("UKHAS sentences must have fields.")
Пример #5
0
        def wrapped(new, old, userctx, secobj):
            new_type = new.get("type", None)
            new_deleted = new.get("_deleted", False)
            if old:
                old_type = old.get("type", None)
            else:
                old_type = None

            # sanity checks
            if old_type is None:
                assert old == {} or old is None
            if new_deleted:
                assert new_type is None

            if new_type == doc_type and old_type in [None, doc_type]:
                # new doc, or modified doc of correct type. validate:
                return func(new, old, userctx, secobj)

            elif new_deleted and old_type == doc_type:
                # deletion is managed by habitat.validate
                return

            elif new_type == doc_type or old_type == doc_type:
                # one or the other types match but not both, and not a new or deleted doc.
                raise ForbiddenError("You cannot change the type of a doc")

            else:
                # other type: not our business
                return
Пример #6
0
def validate_doc(data, schema):
    """Validate *data* against *schema*, raising descriptive errors"""
    v = Validator()
    errors = list(v.iter_errors(data, schema))
    if errors:
        errors = ', '.join((str(error) for error in errors))
        raise ForbiddenError("Validation errors: {0}".format(errors))
    _validate_formats(data, schema) 
Пример #7
0
 def my_validate_func(new, old, userctx, secobj):
     """my docstring"""
     assert userctx == {'roles': ['test role']}
     assert secobj['secobj'] == True
     assert new['type'] == "a_document_type"
     if "check_old" in new:
         assert old == {"type": "a_document_type", "raise": False}
     elif new["raise"]:
         raise ForbiddenError("raising")
Пример #8
0
def _validate_filter(f):
    """
    Check that filters have the required keys according to their type.
    """
    required_keys = {
        'normal': ['filter'],
        'hotfix': ['code', 'signature', 'certificate']}
    for k in required_keys[f['type']]:
        if k not in f:
            raise ForbiddenError(
                "{0} filters must include '{1}'.".format(f['type'], k))
Пример #9
0
def validate(new, old, userctx, secobj):
    """
    Validate this payload_telemetry document against the schema, then perform
    some specific checks:

    * Admins may perform any further editing
    * If edited
        * Only the parser may add new fields to data
        * The receivers list may only get new receivers
    * If created
        * Must have one receiver
        * Must have _raw and nothing but _raw in data
    """
    global schema
    if not schema:
        schema = read_json_schema("payload_telemetry.json")
    validate_doc(new, schema)

    if '_admin' in userctx['roles']:
        return

    expect_id = hashlib.sha256(new['data']['_raw']).hexdigest()
    if '_id' not in new or new['_id'] != expect_id:
        raise ForbiddenError("Document ID must be sha256(base64 _raw data)")

    if old:
        if 'parser' not in userctx['roles'] and \
           not _is_equal_relaxed_floats(new['data'], old['data']):
            raise UnauthorizedError("Only the parser may add data to an"
                                    " existing document.")
        for receiver in old['receivers']:
            if (receiver not in new['receivers'] or
                    new['receivers'][receiver] != old['receivers'][receiver]):
                raise ForbiddenError("May not edit or remove receivers.")
    else:
        if len(new['receivers']) != 1:
            raise ForbiddenError("New documents must have exactly one"
                                 "receiver.")
        if set(new['data'].keys()) - set(('_raw', '_fallbacks')):
            raise ForbiddenError("New documents may only have _raw and/or "
                                 "_fallbacks in data.")
Пример #10
0
def validate(new, old, userctx, secobj):
    """
    Core habitat validation function.

    * Prevent deletion by anyone except administrators.
    * Prevent documents without a type.
    * Prevent documents whose type is invalid.
    * Prevent changing document type.

    """
    if '_deleted' in new:
        must_be_admin(userctx, "Only administrators may delete documents.")
        return

    if 'type' not in new:
        raise ForbiddenError("All documents must have a type.")

    if new['type'] not in allowed_types:
        raise ForbiddenError("Invalid document type.")

    if old and new['type'] != old['type']:
        raise ForbiddenError("Cannot change document type.")
Пример #11
0
 def f(new, old, user, secobj):
     if new == 1:
         assert old == 2
         assert user == 3
         assert secobj == 4
     elif new == "bad":
         from couch_named_python import log, ForbiddenError
         log("Some sort of log")
         raise ForbiddenError("Some sort of error.")
     elif new == "what":
         from couch_named_python import UnauthorizedError
         raise UnauthorizedError("You shall not pass")
     elif new == "meh":
         {"a dict": True}["nonexistant key"]
Пример #12
0
def validate(new, old, userctx, secobj):
    """
    Validate this flight document against the schema, then check that
    only managers are approving documents and approved documents are only
    edited by managers.
    """
    global schema
    if not schema:
        schema = read_json_schema("flight.json")
    validate_doc(new, schema)

    if '_admin' in userctx['roles']:
        return

    if new['approved'] and 'manager' not in userctx['roles']:
        raise UnauthorizedError("Only managers may approve documents.")

    if old and 'manager' not in userctx['roles']:
        raise UnauthorizedError("Only managers may edit documents.")

    start = rfc3339_to_timestamp(new['start'])
    end = rfc3339_to_timestamp(new['end'])
    launch = rfc3339_to_timestamp(new['launch']['time'])
    if start > end:
        raise ForbiddenError("Launch window may not end before it starts.")
    if end - start > 7 * 24 * 3600:
        raise ForbiddenError(
            "Launch window may not be greater than one week"
            " (speak to an admin if you have a special requirement).")
    if not start <= launch < end:
        raise ForbiddenError("Launch time must be within launch window.")

    if 'payloads' in new:
        payloads = new['payloads']
        if len(payloads) != len(set(payloads)):
            raise ForbiddenError("Duplicate entries in payloads list")
Пример #13
0
def _validate_modulation_settings(transmission):
    """
    Check that required keys for each modulation type are present.
    """
    required_keys = {'RTTY': ['shift', 'encoding', 'baud', 'parity', 'stop'],
                     'DominoEX': ['speed'],
                     'Hellschreiber': ['variant']}

    modulation = transmission['modulation']
    if modulation not in required_keys:
        return

    for k in required_keys[modulation]:
        if k not in transmission:
            raise ForbiddenError(
                "{0} transmissions must include '{1}'.".format(modulation, k))