Control de acceso para Flask-Admin

Mi aplicación de matraz se centra en modificar modelos basados en SQLAlchemy. Por lo tanto, creo que flask-admin es un gran complemento porque asigna mis modelos SQLA a formularios con vistas ya definidas con una interfaz personalizable que se prueba y prueba.

Entiendo que Flask-admin está destinado a ser un complemento paraadministradores gestionando los datos de su sitio. Sin embargo, no veo por qué no puedo usar FA como marco para que mis usuarios también CRUDEN sus datos.

Para hacer esto, he escrito lo siguiente:

class AuthorizationRequiredView(BaseView):

  def get_roles(self):
    raise NotImplemented("Override AuthorizationRequiredView.get_roles not set.")

  def is_accessible(self):
    if not is_authenticated():
      return False
    if not current_user.has_role(*self.get_roles()):
      return False
    return True

  def inaccessible_callback(self, name, **kwargs):
    if not is_authenticated():
      return current_app.user_manager.unauthenticated_view_function()
    if not current_user.has_role(*self.get_roles()):
      return current_app.user_manager.unauthorized_view_function()


class InstructionModelView(DefaultModelView, AuthorizationRequiredView):

  def get_roles(self):
    return ["admin", "member"]

  def get_query(self):
    """Jails the user to only see their instructions.
    """
    base = super(InstructionModelView, self).get_query()
    if current_user.has_role('admin'):
      return base
    else:
      return base.filter(Instruction.user_id == current_user.id)

  @expose('/edit/', methods=('GET', 'POST'))
  def edit_view(self):
    if not current_user.has_role('admin'):
      instruction_id = request.args.get('id', None)
      if instruction_id:
        m = self.get_one(instruction_id)
        if m.user_id != current_user.id:
          return current_app.user_manager.unauthorized_view_function()
    return super(InstructionModelView, self).edit_view()

  @expose('/delete/', methods=('POST',))
  def delete_view(self):
    return_url = get_redirect_target() or self.get_url('.index_view')

    if not self.can_delete:
      return redirect(return_url)

    form = self.delete_form()

    if self.validate_form(form):
      # id is InputRequired()
      id = form.id.data

      model = self.get_one(id)

      if model is None:
        flash(gettext('Record does not exist.'), 'error')
        return redirect(return_url)

      # message is flashed from within delete_model if it fails
      if self.delete_model(model):

        if not current_user.has_role('admin') \
            and model.user_id != current_user.id:
          # Denial: NOT admin AND NOT user_id match
          return current_app.user_manager.unauthorized_view_function()

        flash(gettext('Record was successfully deleted.'), 'success')
        return redirect(return_url)
    else:
      flash_errors(form, message='Failed to delete record. %(error)s')

    return redirect(return_url)

Nota: Estoy usando Flask-User que está construido sobre Flask-Login.

El código anterior funciona. Sin embargo, es difícil abstraer como una clase base para otros modelos que me gustaría implementar el control de acceso para operaciones CRUD y vistas de índice / edición / detalle / eliminación.

Principalmente, los problemas son:

el método API,is_accessible, no proporciona la clave principal del modelo. Esta clave es necesaria porque en casi todos los casos las relaciones entre usuarios y entidades casi siempre se almacenan a través de relaciones o en la tabla de modelos directamente (es decir, tener user_id en su tabla de modelos).

algunas vistas, comodelete_view, no proporcione la identificación de instancia que se pueda recuperar fácilmente. Endelete_view, Tuve que copiar toda la función solo para agregar una línea adicional para verificar si pertenece al usuario correcto.

Seguramente alguien ha pensado en estos problemas.

¿Cómo debo reescribir esto en algo que sea más SECO y fácil de mantener?

Respuestas a la pregunta(0)

Su respuesta a la pregunta