Prior to this commit the following situation existed:
> The path /dashboard/analytics/org/:id requires user
> authentication (e.g. signed in). However, it does not enforce
> authorization. Anyone can see this page. The page, however, uses
> javascript to populate the data. So no information, aside from the org
> name associated with the :id leaks out. The javascript API end point
> enforces organization membership.
>
> I would expect that the authorization in the HTML rendering would be
> the same as the javascript API end point.
This commit ensures that the dashboards#analytics end point uses the
same policy logic as the API analytics end points. Further, it keeps
folks who aren't org members out of the base HTML page for other orgs.
Closes forem/forem/#16985
This commit provides two things:
1. Some notes related to my analysis regarding the dashboard
2. Conditional redirects and rendering based on article policies
The code comments say most of what I want to say, but to reiterate:
When a user can't create articles nor do they already have published
articles, then we don't want to avoid showing them stats related to
articles.
Closesforem/forem#16913
Related to forem/forem#16908 and forem/forem#16931
As I'm looking at the dashboard, there's lots of small duplication.
This refactor is a way to help me collect my thoughts regarding how to
approach the larger issue at hand.
There is no reason to create an instance variable as we don't pass this
to the view. Further, by adding this as a before_action there's a
disconnect in logic.
This commit attempts to address those issues.
What follows is a three-fold change:
1. Removing the before action (let's just call the method)
2. Reworking the method to reduce, just a bit, the method cost
3. Removing an unused instance variable
Here are the benchmarks. Note the "follows_limit" as written below is
the "Proposed" route.
```ruby
require 'benchmark'
def follows_limit_original(params:, default: 80, max: 1000)
per_page = (params[:per_page] || default).to_i
@follows_limit = [per_page, max].min
end
def follows_limit(params:, default: 80, max: 1000)
return default unless params.key?(:per_page)
per_page = params[:per_page].to_i
return max
per_page
end
def follows_limit_alt(params:, default: 80, max: 1000)
per_page = params.fetch(:per_page, default).to_i
return max if per_page > max
per_page
end
TIMES = 10_000
Benchmark.bmbm do |b|
b.report("Original no params") { 1000.times { follows_limit_original(params: {}) } }
b.report("Original less than max") { 1000.times { follows_limit_original(params: {per_page: 90 }) } }
b.report("Original greater than max") { 1000.times { follows_limit_original(params: {per_page: 9000 }) } }
b.report("Proposed no params") { 1000.times { follows_limit(params: {}) } }
b.report("Proposed less than max") { 1000.times { follows_limit(params: {per_page: 90 }) } }
b.report("Proposed greater than max") { 1000.times { follows_limit(params: {per_page: 9000 }) } }
b.report("Alt no params") { 1000.times { follows_limit_alt(params: {}) } }
b.report("Alt less than max") { 1000.times { follows_limit_alt(params: {per_page: 90 }) } }
b.report("Alt greater than max") { 1000.times { follows_limit_alt(params: {per_page: 9000 }) } }
end
```
```shell
$ ruby /Users/jfriesen/git/forem/bench.rb
Rehearsal -------------------------------------------------------------
Original no params 0.000403 0.000002 0.000405 ( 0.000405)
Original less than max 0.000109 0.000005 0.000114 ( 0.000114)
Original greater than max 0.000161 0.000006 0.000167 ( 0.000166)
Proposed no params 0.000076 0.000001 0.000077 ( 0.000076)
Proposed less than max 0.000114 0.000009 0.000123 ( 0.000126)
Proposed greater than max 0.000112 0.000011 0.000123 ( 0.000123)
Alt no params 0.000104 0.000007 0.000111 ( 0.000114)
Alt less than max 0.000113 0.000009 0.000122 ( 0.000122)
Alt greater than max 0.000111 0.000001 0.000112 ( 0.000115)
---------------------------------------------------- total: 0.001354sec
user system total real
Original no params 0.000108 0.000000 0.000108 ( 0.000110)
Original less than max 0.000109 0.000001 0.000110 ( 0.000111)
Original greater than max 0.000109 0.000000 0.000109 ( 0.000109)
Proposed no params 0.000071 0.000000 0.000071 ( 0.000071)
Proposed less than max 0.000103 0.000001 0.000104 ( 0.000103)
Proposed greater than max 0.000102 0.000000 0.000102 ( 0.000103)
Alt no params 0.000093 0.000000 0.000093 ( 0.000093)
Alt less than max 0.000103 0.000000 0.000103 ( 0.000104)
Alt greater than max 0.000102 0.000000 0.000102 ( 0.000103)
```
There is an incredible amount of conditionals in play within this
controller. I'm working to disentangle the logic so we can introduce
unified authorization policies.
The first step is removing the quasi-opaque "before_action" behavior
related to authorization. My strong preference is to make that explicit
and in doing so begin to see how to adjust the policy enforcement/creation.
This relates to forem/forem#16913
* In dashboard show api eager load collection with articles
* Dashboard Article Row view now loads collection to suppress bullet warning of eager loading
* Remove pro role on user, expose pro dashboard to all users as analytics
* Remove pro from Elasticsearch mappings
* Update user role docs to use :trusted over :pro
* Remove pro from Role model spec
* Remove more references to pro, as noted by @rhymes
* Fixes unsafe relection warning @ dashboards_controller
This one is purely to make CodeClimate happy as the code that was
already there is equivalent (as the parameter being `constantize`'d was
validated before the reflection part kicked in).
I've switch things around and do a lookup on the
`UserSubscription::ALLOWED_TYPES` for the subscription `source_type`,
which makes brakeman understand that type comes from an allowed list of
types (instead the user input).
* Flips conditional statement when no source_type to unless
Co-authored-by: rhymes <rhymes@hey.com>
Co-authored-by: rhymes <rhymes@hey.com>
* Remove mock from specs to test the actual Suggester
* Fix articles API top articles n+1
* Fix n+1 in articles API with state parameter
* Remove eager loading for organization dashboard
* Eager load notifiables in notifications only when needed
* Algolia does not like this
* Move from env variables to SiteConfig
Related to #5384
This PR only deals with the first remaining part outlined in the issue, starting to use existing SiteConfig keys instead of the env variables.
* Restore Envfile to original version for now
* Add infinite scroll to followers list (#258)
* Refactor fetchNext function for clarity (#258)
* Sepparate following tab into multiple tabs to support infinite scroll (#258)
* Add infinite scroll to following pages (#258)
* Add tests to infinite scroll api
* Refactor dashboard loading text
* Refactor infine scroll function
* Fix duplicated entries problem in infinite scrolling
* Switch randomized attributes to sequential to avoid InvalidRecord error
* Add acceptance tests for infinite scroll
* Remove unused following method
* parameterize limit per page for followers and followings
* Split follows endpoint into followers and followings
* Authenticate user with api key in followers and followings
* Split followers endpoint into users and organizations
* Add redundant html to sublist partial
* Speed up infinite scroll tests
* Refactor api json responses to use partials
* Authenticate api user before follows create
* Resolve conflicts on scrolling js
* Improve partials organization and fix organization username bug
* Improve readability of unauthorized test
* Use let! to create scrolling test data
* Fix not working podcasts link
* Refactor initScrolling to remove linting errors
* Fix codeclimate coding style issue
* Test tags forms and podcasts hyperlinks
* Fix eslint issue with double equals
* Improve before_action usage and readability
* Move delayed calls for update analytics to Active job
- add job + job spec
- refactoring
* update namings+specs due to requested changes after review
* Fixes on analytics job spec + minor refactor of service
* Fix JS lint issues
* Do not rely on globals
* Use InstantClick to make transitions work in the pro dashboard
* Fix eager loadings and remove unused queries
* Refactor drawChart
* More consistency in naming
* Display correct analytics for organization
* Allow user to have many orgs
* Allow users to handle multi orgs in settings
* Make rounded buttons inline
* Add multi org function to dashboards
* Fix merge conflicts
* Fix mistake in merge conflict fix oops
* Display the correct membership level
* Fix accessibility issues
* Display organizations for article editors
* Handle submitting org id with preact editors
* Make listings work with multiple organizations
* Allow listings to have multiple orgs on create
* Display the correct number of credits for each org
* Move script tag to Webpack
* Allow multi orgs for purchasing and viewing credits
* Use OrganizationMembership as authorization check
* Display multiple organizations for notifications
* Allow dashboard to be viewable under multi-orgs
* Remove unused method
* Add multi-org functionality for article editors
* Show pro dashboard buttons for member+ org levels
* Leave the correct organization
* Allow article API to change org id
* Add left-out authorization method oops
* Make nav buttons a bit more clear
* Fix merge conflict
* Fix adding org id for /api/articles and tests
* Fix tests for org policy
* Use proper logic for displaying org members
* Update org actions with new authorization
* Use correct org when creating a listing
* Remove additional payment charge oops
* Mark org notifications as read with authorization
* Remove deprecated post_as_organization attribute
* Use new org_admin syntax
* Remove deprecated org logic for article create and update
* Default all RSS posts to not belong to any org
* Render org_member page for guest users
* Update org policy spec to work with multi orgs
* Use org_membership for org traits and move identity code
* Use org_member trait
* Update to work with multi-orgs
* Validate article's org_id if param org_id is blank
* Make a let variable
* Remove unnecessary eager load for credits
* Fix HTML structure and org logic for non-org users
* Update credits spec for multi-org
* Add test for failed payment when purchased by org
* Lint listings_spec
* Test that the listing was created under the user
* Add tests for POST /listings multi-org
* Use double quotes for classes
* Fix /manage and a few other multi-org bugs
* Fix test for multi org
* Use correct method SQL exists? not Rails exist?
* Fix reads spec for multi-org
* Fix org_controller actions to work with multi org
* Test only multi org and not old usage and fix leave_org
* Fix org showing user profile img test for multi-org
* Fix org logic for users with no orgs
* Remove switch org functionality
* Update tests and add hidden param for org id
* Redirect to the specific organization
* Test other org button actions
* Use settings_notice instead of legacy notice and refactor
* Fix weird extra end issue prob from merge conflicts
* Test for with new flash key
* Fix user_views_org tests for multi-org
* Test for new flash message
* Update snapshot with new a11y html
* Move styling to stylesheet
* Add site admins functionality
* Move org_member? method in user model and refactor
* Use unspent_credits_count for organizations
* Add tests for /listings/new and minor bug fixes
* Use .present? in case of empty array
* Fix a lingering deprecated method
* Use greater than 1 for random numbers
* Add tests for counting spent and unspent credits
* Preload organization, not user
* Preload users only when needed
* Pre-load podcasts for podcast episodes in API
* Avoid eager loading error by loading rating votes separately
* Preload associations for moderation
* Preload user comments in trees
* Preload organization for non org dashboard and cleanup queries
* Optimize ArticleSuggester to only load N articles at need
* Remove eager loading and pass variables to partials for easier debug
* Reorganize tags validation code and ignore actsastaggableon eager loading issues
* Remove unused eager loading and bring up comments relation
* Preload podcasts when loading podcast episodes
* Fix views specs
* Make sure ArticleSuggester never returns duplicates
* Remove commented code
* Re-trigger build
* Move suggested articles back to view to respect fragment caching
* be able to sort dashboard articles (#1837)
* remember to escape order clause is arel (#1837)
* add back in missing character for created_at (#1837)
* use case statement to avoid threat of injection (#1837)
* write specs to ensure sorting works for dashboard articles (#1837)
* add publish sort options, refactor sort options into articles helper (#1837)
* refactor to use model scope (#1837)
* Refactor and optimize article ids a bit
* Add chartJS to package.json
* Add new queries for charts
* Add past week charts for rxns and comments
* Use real data oops
* Round followers percent to 2 decimals
Co-Authored-By: Zhao-Andy <andyzhao.zhao@gmail.com>
* Round all percentages to 2 decimals
Co-Authored-By: Zhao-Andy <andyzhao.zhao@gmail.com>
* allow admins to see dashboards
* add more info to internal users
* add more info to internal users
* add video permission toggle
* specify banished user traits
* Add point weights for tag follows
* Adjust num articles to initially show up on home page
* Fix schema.rb
* Adjust follow policy spec
* Adjust dashboard styling for follow points form
* Initial automatic cleanup with rubocop
* Fix syntax error introduced by rubocop
* Cleanup seeds file
* Cleanup lib folder
* Exclude bin folder because it contains auto generated files
* Make Rubocop a little bit more chatty
* Block length should not include comments in the count
* Cleanup config folder
* Cleanup specs
* Updated Rubocop version and generated a todo file
* Fix broken ArticlesApi spec
* Fix tests
* Restored rubocop pre-commit hook
* added organization policy + spec
* user specs for is_org_admin?
* added authroize to organization controller
* admin policy + specs
* deleted enforce admin due to pundit policy redundancy
* applied admin policy to entire admin namespace
* refactoring analytics controller WIP - wanna test codeship
* Add protection against reactions to unpublished articles (#473)
* Add chat channel policy and spec (#474)
* Add comment policy and specs (#475)
* Fix edge case with apostrophes
* Add comment policy and specs
* Add login for deleting comment spec
* Change test to raise pundit error instead of 404
* Clean up comment destroy request specs
* Remove redundant raise
* Whitelist columns on to_json call (#477)
* Add pundit policy for several controllers (#476)
* Add pundit policy for several controllers
* Adjust video spec
* Fix tag request specs
* Add proper twilio tokens request specs
* Remove puts statements
* Add a couple basic request specs (#478)
* Add a few tests and fix user tag color bug (#482)
* Refactor handle_tag_index in stories_controller (#481)
* Modify valid_request_origin? (#483)
* Add misc specs and remove banned attribute from user model (#484)
* Fix missing Cloudinary tags and misc specs (#486)
* removing current_user_is_admin? to use .is_admin? method
* added missing org policy routes
* Add comment for all public controllers
* Fix edge case for test
* Authorize mod controller and add specs
* Refactor methods via inheritance and use only super_admin role
* Create policy method for analytics via article_policy and refactor
* Capitalize all buttons in dashboard page
* Fix org tests and remove old admin test
* Use only happy path for analytics
* Fix tests to use Pundit error
* Update org_policy spec
* Add comment for public controllers
* Add block policy and specs
* Add policy for authorizing dashboard
* Add follow policy and specs
* Refactor a tiny bit
* Prevent banned users from following anything
* Add a note for email sub controller about auth
* Add image upload policies
* Add policy for github repos
* Fix typo and use correct github repo variable
* Fix image uploader and use regular params
* Add authenticate_user before action back in
* Rename test
* Update slack bot message formatting and fix reported URL
* Remove email blank onboarding redirect
* Add authorization specs for video upload
* Add registration spec
* Remove skip before actions for signup complete
* Use new param for abuse reports
* Remove else because it never runs
* Add more dashboard request specs
* Use let with no bang in case there are errors