Example #1
0
def delete(capsule_id, sshkey_id, user):
    capsule = _get_capsule(capsule_id, user)
    try:
        sshkey = SSHKey.query.get(sshkey_id)
    except StatementError:
        raise BadRequest(description=f"'{sshkey_id}' is not a valid id.")

    if sshkey is None:
        raise NotFound(description=f"The requested sshkey '{sshkey_id}' "
                       "has not been found.")

    if sshkey not in capsule.authorized_keys:
        raise BadRequest
    elif (sshkey.user_id is not None) or (len(sshkey.capsules) > 1):
        # sshkey is linked to a user or in other(s) capsule(s)
        capsule.authorized_keys.remove(sshkey)
    else:  # sshkey is juste present for this capsule
        db.session.delete(sshkey)

    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    return None, 204
Example #2
0
def post(capsule_id, user, cron_data=None):
    capsule = _get_capsule(capsule_id, user)
    webapp = capsule.webapp

    if len(webapp.crons) > 0:
        raise Forbidden(description="Only one cron per webapp is allowed")

    if cron_data is None:
        cron_data = request.get_json()

    data = cron_schema.load(cron_data)
    cron = Cron(**data)
    webapp.crons.append(cron)

    db.session.add(cron)
    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    result_json = cron_schema.dump(cron)
    return result_json, 201, {
        'Location':
        f'{request.base_url}/capsules/{capsule_id}/webapp/{cron.id}',
    }
Example #3
0
def delete(capsule_id, user_id, user):
    capsule = _get_capsule(capsule_id, user)

    if len(capsule.owners) == 1:
        raise Forbidden(description="A capsule need one owner at least !")

    if user_id == user.name:
        raise Conflict(description='Cannot delete yourself from owners')

    user_is_owner = False
    user_id_in_owners = False
    for owner in capsule.owners:
        if user.name == owner.name:
            user_is_owner = True
        if user_id == owner.name:
            user_id_in_owners = True

    if not user_id_in_owners:
        raise NotFound(description=f'{user_id} is not in owners.')

    if (not user_is_owner) and (user.role == RoleEnum.user):
        raise Forbidden()

    user = User.query.filter_by(name=user_id).one_or_none()
    if user is None:  # pragma: no cover
        raise NotFound(description=f'{user} is an invalid user.')

    capsule.owners.remove(user)
    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    return None, 204
Example #4
0
def post(capsule_id, user):
    sshkey_data = request.get_json()
    capsule = _get_capsule(capsule_id, user)

    for public_key in sshkey_data:
        if not valid_sshkey(public_key):
            raise BadRequest(description=f"'{public_key}' is not "
                             "a valid ssh public key")

        for key in capsule.authorized_keys:
            if public_key == key.public_key:
                raise Conflict(description="'public_key' already exist "
                               "for this capsule")

        sshkey = SSHKey(public_key=public_key)
        capsule.authorized_keys.append(sshkey)

    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    result = Capsule.query.filter_by(id=capsule_id).first()
    return capsule_output_schema.dump(result), 201, {
        'Location': f'{request.base_url}/capsules/{capsule.id}',
    }
Example #5
0
def post(capsule_id, user):
    capsule = _get_capsule(capsule_id, user)
    if user.role < RoleEnum.admin and not capsule.delegate_tls:
        raise Forbidden

    fqdn_data = request.get_json()
    data = fqdn_schema.load(fqdn_data)

    existing_fqdn = FQDN.query.filter_by(name=data['name']).one_or_none()
    if existing_fqdn is not None:
        raise BadRequest(description=f'{data["name"]} already exists.')

    fqdn = FQDN(**data)
    capsule.fqdns.append(fqdn)

    db.session.add(fqdn)
    db.session.commit()

    webapp = capsule.webapp
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)) and\
       webapp is not None:
        nats.publish_webapp_present(capsule)

    result = fqdn_schema.dump(fqdn)
    return result, 201, {
        'Location':
        f'{request.base_url}/capsules/{capsule.id}/fqdns/{fqdn.id}',
    }
Example #6
0
def delete(capsule_id, fqdn_id, user):
    capsule = _get_capsule(capsule_id, user)
    if user.role < RoleEnum.admin and not capsule.delegate_tls:
        raise Forbidden

    if len(capsule.fqdns) == 1:
        raise Forbidden(description="A webapp need at least one FQDN.")

    try:
        fqdn = FQDN.query.get(fqdn_id)
    except StatementError:
        raise BadRequest(description=f"'{fqdn_id}' is not a valid id.")

    if not fqdn:
        raise NotFound(description=f"The requested FQDN '{fqdn_id}' "
                       "has not been found.")

    db.session.delete(fqdn)
    db.session.commit()

    webapp = capsule.webapp
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)) and\
       webapp is not None:
        nats.publish_webapp_present(capsule)

    return None, 204
Example #7
0
def nats_publish_webapp_present(user):
    query = []
    query.append(Capsule.owners.any(User.name == user.name))
    capsules = Capsule.query.filter(*query).all()
    now = datetime.datetime.now()
    for capsule in capsules:
        if now > (capsule.no_update + datetime.timedelta(hours=24)):
            nats.publish_webapp_present(capsule)
Example #8
0
def put(capsule_id, user):
    capsule = _get_capsule(capsule_id, user)
    webapp = capsule.webapp
    webapp_data = request.get_json()

    # PUT become POST if there is no webapp
    if webapp is None:
        return post(capsule_id=capsule_id, webapp_data=webapp_data)

    data = webapp_schema.load(webapp_data)

    webapp.env = None
    if "env" in webapp_data:
        webapp.env = webapp_data["env"]

    # Ensure new runtime_id has same familly
    new_runtime_id = str(data["runtime_id"])
    try:
        new_runtime = Runtime.query.get(new_runtime_id)
    except StatementError:
        raise BadRequest(description=f"'{new_runtime_id}' is not a valid id.")
    if new_runtime is None:
        raise BadRequest(description=f"The runtime_id '{new_runtime_id}' "
                         "does not exist.")
    new_fam = str(new_runtime.fam)
    old_fam = str(webapp.runtime.fam)
    if new_fam != old_fam:
        raise BadRequest(f"Changing runtime familly from '{old_fam}' "
                         f"to '{new_fam}' is not possible")
    webapp.runtime_id = data["runtime_id"]

    webapp.opts = []
    if "opts" in data:
        opts = Option.create(data["opts"], data["runtime_id"], user.role)
        webapp.opts = opts

    if "volume_size" in data:
        if (user.role is RoleEnum.user) and (not user.parts_manager):
            raise Forbidden(description='You cannot set webapp volume size.')

        remaining_size = getWebappsVolumeUsage(str(webapp.id))
        target_size = data['volume_size'] + remaining_size
        if target_size > current_app.config['VOLUMES_GLOBAL_SIZE']:
            msg = 'Please set a lower volume size for this webapp or prepare '\
                'some Bitcoins... :-)'
            raise PaymentRequired(description=msg)
        webapp.volume_size = data['volume_size']

    capsule.webapp = webapp
    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    return get(capsule_id)
Example #9
0
def patch(capsule_id, user):
    capsule = _get_capsule(capsule_id, user, check_delegate=True)
    data = request.get_json()

    if ("key" in data and "crt" not in data) or \
            ("crt" in data and "key" not in data):
        raise BadRequest(description="Both crt and key are "
                         "required together")

    if "crt" in data and "key" in data:
        try:
            str_cert = base64.b64decode(data['crt'])
            str_key = base64.b64decode(data['key'])
        except binascii.Error:
            raise BadRequest(description="'crt' and 'key' must be "
                             "base64 encoded.")
        try:
            # Ensure that certificate and key are paired.
            if not is_keycert_associated(str_key, str_cert):
                raise BadRequest(description="The certificate and the key "
                                 "are not associated")
        except NotValidPEMFile:  # Possible ??
            raise BadRequest
        capsule.tls_crt = data["crt"]
        capsule.tls_key = data["key"]

    if "force_redirect_https" in data:
        if data['force_redirect_https']:
            capsule.enable_https = True
        capsule.force_redirect_https = data["force_redirect_https"]

    if "enable_https" in data:
        if not data['enable_https']:
            capsule.force_redirect_https = False
        capsule.enable_https = data["enable_https"]

    db.session.commit()

    webapp = capsule.webapp
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)) and\
       webapp is not None:
        nats.publish_webapp_present(capsule)

    result = capsule_output_schema.dump(capsule)
    return result, 200, {
        'Location': f'{request.base_url}/capsules/{capsule.id}',
    }
Example #10
0
def delete(capsule_id, user):
    capsule = _get_capsule(capsule_id, user, check_delegate=True)

    if capsule.enable_https:
        msg = 'Please, disable HTTPS for this capsule before '\
              'trying to remove the certificate.'
        raise Conflict(description=msg)

    capsule.tls_crt = None
    capsule.tls_key = None

    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    return None, 204
Example #11
0
def patch(capsule_id, user):
    capsule = _get_capsule(capsule_id, user)
    owner_data = request.get_json()

    if "newOwner" not in owner_data:
        raise BadRequest("The key newOwner is required.")
    new_owner = owner_data["newOwner"]

    user_is_owner = False
    for owner in capsule.owners:
        if user.name == owner.name:
            user_is_owner = True
        if new_owner == owner.name:
            msg = f'{new_owner} is already in owners list!'
            raise Conflict(description=msg)

    if (not user_is_owner) and (user.role == RoleEnum.user):
        raise Forbidden

    try:  # Check if owners exist on Keycloak
        check_owners_on_keycloak([new_owner])
    except KeycloakUserNotFound as e:
        raise NotFound(description=f'{e.missing_username} was not '
                                   'found in Keycloak.')

    # Get existent users, create the others
    user = User.query.filter_by(name=new_owner).one_or_none()
    if user is None:  # User does not exist in DB
        new_user = User(name=new_owner, role=RoleEnum.user)
    else:
        new_user = user

    capsule.owners.append(new_user)
    db.session.commit()

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    result = Capsule.query.filter_by(id=capsule_id).first()
    return capsule_output_schema.dump(result), 200, {
        'Location': f'{request.base_url}/{capsule.id}',
    }
Example #12
0
def put(capsule_id, cron_id, user):
    capsule = _get_capsule(capsule_id, user)
    webapp = capsule.webapp

    cron_data = request.get_json()
    data = cron_schema.load(cron_data)

    try:
        cron = Cron.query.get(cron_id)
    except StatementError:
        raise BadRequest(description=f"'{cron_id}' is not a valid id.")

    if not cron:
        raise NotFound(description=f"The requested cron '{cron_id}' "
                       "has not been found.")

    if str(cron.webapp_id) != str(webapp.id):
        raise Forbidden

    cron.command = data['command']
    data.pop('command')
    cron.minute = "0"
    if 'minute' in data:
        cron.minute = data['minute']

    keys = ['hour', 'month', 'month_day', 'week_day']
    for key in keys:
        if key in data:
            setattr(cron, key, data[key])
        else:
            setattr(cron, key, "*")

    db.session.commit()
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)
    return get(capsule_id, cron_id)
Example #13
0
def delete(capsule_id, cron_id, user):
    capsule = _get_capsule(capsule_id, user)
    webapp = capsule.webapp

    try:
        cron = Cron.query.get(cron_id)
    except StatementError:
        raise BadRequest(description=f"'{cron_id}' is not a valid id.")

    if not cron:
        raise NotFound(description="This cron is not present in this webapp")

    if str(cron.webapp_id) != str(webapp.id):
        raise Forbidden

    db.session.delete(cron)
    db.session.commit()

    # TODO: we may need another message to delete the cron here
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    return None, 204
Example #14
0
def put(capsule_id, fqdn_id, user):
    capsule = _get_capsule(capsule_id, user)
    if user.role < RoleEnum.admin and not capsule.delegate_fqdns:
        raise Forbidden

    fqdn_data = request.get_json()
    data = fqdn_schema.load(fqdn_data)

    try:
        fqdn = FQDN.query.get(fqdn_id)
    except StatementError:
        raise BadRequest(description=f"'{fqdn_id}' is not a valid id.")

    if not fqdn:
        raise NotFound(description=f"The requested FQDN '{fqdn_id}' "
                       "has not been found.")

    fqdn.alias = data["alias"]
    fqdn.name = data["name"]

    try:
        db.session.commit()
    except IntegrityError:
        raise BadRequest(description=f"'{data['name']}' already exists.")

    webapp = capsule.webapp
    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)) and\
       webapp is not None:
        nats.publish_webapp_present(capsule)

    result = fqdn_schema.dump(fqdn)
    return result, 200, {
        'Location':
        f'{request.base_url}/capsules/{capsule.id}/fqdns/{fqdn.id}',
    }
Example #15
0
def post(capsule_id, user, webapp_data=None):
    capsule = _get_capsule(capsule_id, user)

    if len(capsule.fqdns) == 0:
        raise Conflict(description="A webapp need at least one FQDN.")

    webapp = capsule.webapp

    # Only one webapp per capsule
    if webapp is not None:
        raise Conflict(description="This capsule already has a webapp.")

    # Datas could come from PUT
    if webapp is None:
        webapp_data = request.get_json()
    data = webapp_schema.load(webapp_data)

    runtime_id = data["runtime_id"]
    try:
        runtime = Runtime.query.get(runtime_id)
    except StatementError:
        raise BadRequest(description=f"'{runtime_id}' is not a valid id.")

    if runtime is None:
        raise BadRequest(description=f"The runtime_id '{runtime_id}' "
                         "does not exist.")

    if runtime.runtime_type is not RuntimeTypeEnum.webapp:
        raise BadRequest(description=f"The runtime_id '{runtime.id}' "
                         "has not type 'webapp'.")

    newArgs = dict()

    if "opts" in data:
        opts = Option.create(data["opts"], runtime_id, user.role)
        data.pop("opts")
        newArgs["opts"] = opts

    if "volume_size" not in data:
        data['volume_size'] = current_app.config['VOLUMES_DEFAULT_SIZE']
    else:
        if (user.role is RoleEnum.user) and (not user.parts_manager):
            raise Forbidden(description='You cannot set webapp volume size.')

    remaining_size = getWebappsVolumeUsage()
    target_size = data['volume_size'] + remaining_size
    if target_size > current_app.config['VOLUMES_GLOBAL_SIZE']:
        msg = 'Please set a lower volume size for this webapp or prepare '\
              'some Bitcoins... :-)'
        raise PaymentRequired(description=msg)

    webapp = WebApp(**data, **newArgs)
    capsule.webapp = webapp

    db.session.add(webapp)
    db.session.commit()

    result = WebApp.query.get(capsule.webapp_id)

    now = datetime.datetime.now()
    if now > (capsule.no_update + datetime.timedelta(hours=24)):
        nats.publish_webapp_present(capsule)

    # Api response
    result_json = webapp_schema.dump(result)

    return result_json, 201, {
        'Location': f'{request.base_url}/{capsule.id}/webapp',
    }