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
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}', }
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
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}', }
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}', }
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
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)
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)
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}', }
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
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}', }
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)
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
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}', }
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', }