Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data filtering for rails backend #16

Merged
merged 12 commits into from
Sep 9, 2021

Conversation

cofinalsubnets
Copy link
Contributor

No description provided.

backends/rails/app/controllers/repos_controller.rb Outdated Show resolved Hide resolved
backends/rails/app/models/issue.rb Show resolved Hide resolved
backends/rails/app/controllers/orgs_controller.rb Outdated Show resolved Hide resolved
role in actor.org_roles and
role_name = role.name and
resource = role.org;
resource.id = role.org_id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it not work with resource = role.org?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, somehow it hit one of the unimplemented paths in the core

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you created a task for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will once I find a minimal repro 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this happen?

Copy link
Member

@gj gj Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping @GW000

backends/rails/lib/fetcher.rb Outdated Show resolved Hide resolved
backends/rails/config/initializers/oso.rb Outdated Show resolved Hide resolved
repo_id: Integer,
user_id: Integer,
name: String,
repo: Relationship.new(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to build these relationships automatically from the activerecord associations? 😬

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally fine if that's out of scope, just kind of curious

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that sure would be nice, this is a lot of boilerplate!

orgs = Org.all
orgs = orgs.filter { |org| OSO.allowed?(actor: current_user, action: "read", resource: org) }
render json: orgs
render json: OSO.get_allowed_resources(current_user, 'read', Org)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only place we actually use data filtering

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add it to the repos index too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the repos index is populated based on org ownership though, not user permissions.

Copy link
Contributor Author

@cofinalsubnets cofinalsubnets Aug 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add another view I guess that shows all the repos for the current user

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably worth doing anyway -- it's a good way of showing how data filtering is doing something more than a simple filter.

But even if repos are filtered by org, you might want to apply data filtering. For example, in GitHub you have public + private org repos that you can see or not depending on whether you're a member of the org.
That's not the case in the current gitclub, but is rationale for why you would still apply authorization to the org_repos index.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok the new view is in, it's a good example for data filtering because it shows repos from any org. As it is only org members can view the org repos index so the authorization is already implicit there. The current data filtering API isn't a good fit here unfortunately, you'd need to get the repos the user can read, then filter it again to select the ones belonging to the org.

Copy link
Contributor Author

@cofinalsubnets cofinalsubnets left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reflexively pressed the big green button and am not qualified to review my own code 👍

@@ -5,7 +5,7 @@ class ApplicationController < ActionController::API
rescue_from Exceptions::Forbidden, with: :render_forbidden

def authorize!(action, resource, error=Exceptions::NotFound)
raise error unless OSO.allowed?(actor: current_user, action: action, resource: resource)
raise error unless OSO.allowed?(actor: current_user, action: action.to_s, resource: resource)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so action can be a symbol, otherwise the auth just fails

backends/rails/app/policy/authorization.polar Outdated Show resolved Hide resolved
Copy link
Contributor

@gkaemmer gkaemmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, my only feedback would be to add longer names and maybe some comments to fetcher.rb -- my guess is we'll be pointing a lot of people towards that file as a way to teach them the concepts

kinds = Hash.new { |k| raise "Unsupported constraint kind: #{k}" }
kinds['Eq'] = kinds['In'] = ->(q, c) { q.where param[c] }
kinds['Neq'] = ->(q, c) { q.where.not param[c] }
it['Eq'] = it['In'] = ->(q, c) { q.where param[c] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feeling like this file might be sacrificing some readability for conciseness/performance. IMO the goal here is that someone can look at this file and use it to build their own fetchers, so it's probably worth lengthening variable names, adding some comments, etc.

I took a stab at restructuring it with some longer variable names and inlining the instance_variable_set. What do you think?

module Fetcher
  def self.included(base) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
    base.class_eval do
      to_activerecord_clause = lambda do |constraint|
        if constraint.field.nil?
          # When field is empty, we should match using ID
          { primary_key => constraint.value.send(primary_key) }
        else
          { c.field => c.value }
        end
      end

      @constraint_handlers = {
        'Eq': ->(query, constraint) { query.where to_activerecord_clause[constraint] },
        'In': ->(query, constraint) { query.where to_activerecord_clause[constraint] },
        'Neq': ->(query, constraint) { query.where.not to_activerecord_clause[constraint] }
      }
      @constraint_handlers.default_proc = proc { |k| "Unsupported constraint kind: #{k}" }
      @constraint_handlers.freeze

      def self.build_query(constraints)
        constraints.reduce(all) do |query, c|
          @constraint_handlers[constraint.kind][query, constraint]
        end
      end

      def self.exec_query(query)
        query.distinct.to_a
      end

      def self.combine_query(one, two)
        one.or(two)
      end
    end
  end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right, I'll change this 👍

actor_has_role_for_resource(org: Org, _role: String, repo: Repo) if
repo.org = org;
actor_has_role_for_resource(_: User{repo_roles: roles}, name: String, repo: Repo) if
role in roles and role matches { name: name, repo: repo };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@cofinalsubnets cofinalsubnets changed the base branch from main to release-0.20.0 September 8, 2021 15:08
@gj
Copy link
Member

gj commented Sep 8, 2021

@GW000 looks like #15 is redundant after f4d7b94

@cofinalsubnets cofinalsubnets merged commit c226c0d into release-0.20.0 Sep 9, 2021
@cofinalsubnets cofinalsubnets deleted the gwen/rails-data-filtering branch September 9, 2021 20:51
@cofinalsubnets cofinalsubnets mentioned this pull request Sep 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants