Skip to content

Private spaces implementation#1339

Open
valentinRyckaert wants to merge 92 commits into
ElixirTeSS:masterfrom
valentinRyckaert:private-spaces-with-groups
Open

Private spaces implementation#1339
valentinRyckaert wants to merge 92 commits into
ElixirTeSS:masterfrom
valentinRyckaert:private-spaces-with-groups

Conversation

@valentinRyckaert

Copy link
Copy Markdown
Contributor

Summary of changes

Database

3 new tables

  • groups:
    • id: bigint
    • title: string
  • groups_membership:
    • user_id: bigint
    • group_id: bigint
    • owner: boolean
  • groups_spaces:
    • group_id: bigint
    • space_id: bigint

Changes in existing tables

  • spaces:
    • new - is_private: boolean

Code

New group controller and policy

  • everyone can see all groups (like users)
  • only members and admins of a group can see the group details page
  • only owners and admins can edit/destroy a group (and so add people)
  • only admins can create a group
  • cant create/edit/destroy by API

New MemberShip Model

  • primary key: group_id, user_id
  • belongs to user
  • belongs to group

Shown? in application policy

  • show if:
    • the record does not belong to a space
    • the space of the record is public
    • the record is a space (so the space list can be shown in the main space)
    • the space of the record is the current space and user is in at least one of the required groups for the space (the user needs to be connected) or the user is an admin (admin have so full rights)

Spaces controller and policy updates

  • space policy:

    • everyone can see public spaces
    • only members of required groups can see private spaces
    • only space admins/ main admins can edit space
    • only main admins can create/destroy space
  • space controller:

    • index: filter shown spaces with shown? policy function

Solr search_index updates

  • uses shown? to filter the ressources list to show (index view of materials, events and spaces)

Application controller updates

  • check if the user has the right to access current_space (private space) with the shown? function and show default space if not

Space model update

  • all ressources form a private space are destroyed if the space is destroyed
  • if the space is public, all the ressources except roles (destroyed) are nullified
  • when nullified, we reload Solr index to make changes visible

Rdoc

  • docstrings
  • documentation about followed conventions

Screenshots

navbar group form group-2

Checklist

  • I have read and followed the CONTRIBUTING guide.
  • I confirm that I have the authority necessary to make this contribution on behalf of its copyright owner and agree to license it to the TeSS codebase under the BSD license.

Valentin Ryckaert and others added 30 commits June 12, 2026 09:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces “private spaces” backed by a new group/membership system, and threads the resulting access-control rules through Pundit policies, controllers (space resolution + search/index filtering), UI, and tests.

Changes:

  • Adds Group/GroupMembership models + CRUD UI, plus authorization rules that allow read access to members/admins and restrict write access to owners/admins (and block API writes).
  • Adds spaces.is_private and space↔group association, with shared ApplicationPolicy#shown? visibility logic and private-space enforcement in ApplicationController#set_current_space.
  • Filters indexed/search results (incl. Solr-backed listings) by shown?, and adds documentation + test coverage for the private-space access scenario.

Reviewed changes

Copilot reviewed 53 out of 55 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
test/system/groups_test.rb System tests for basic Groups UI flows (index/create/update/destroy).
test/models/space_test.rb Model validation test for “private space must have required groups”.
test/models/group_test.rb Placeholder Group model test file.
test/integration/private_space_access_test.rb Integration-style coverage for private-space access across spaces/materials surfaces.
test/fixtures/groups.yml Adds Group fixtures used by tests.
test/controllers/groups_controller_test.rb Controller-level tests for Group authorization + API write restrictions.
Gemfile Adds explicit rdoc (>= 8.0) dependency.
Gemfile.lock Updates lockfile for rdoc/rbs/prism-related dependencies.
docs/spaces.md Documents private spaces behavior and deletion/nullification semantics.
docs/docstrings.md Adds RDoc/docstring conventions documentation.
db/schema.rb Adds groups, group_memberships, groups_spaces, and spaces.is_private to schema.
db/migrate/20260612065213_create_groups.rb Creates groups table.
db/migrate/20260612065245_create_join_table_groups_users.rb Creates interim groups↔users join table (later replaced).
db/migrate/20260612065251_create_join_table_groups_spaces.rb Creates groups↔spaces join table.
db/migrate/20260618090239_add_is_private_to_spaces.rb Adds is_private boolean to spaces.
db/migrate/20260618141208_replace_groups_users_with_group_memberships.rb Replaces groups_users with group_memberships join model (with owner flag).
config/routes.rb Adds RESTful routes for groups.
config/locales/en.yml Updates “spaces” copy to mention private spaces/groups.
config/application.rb Whitespace-only change.
app/views/users/show.html.erb Adds a “Groups” section to the user sidebar with collapse/overflow UI.
app/views/users/_list.json.jbuilder Alters /users.json list output (now excluding admins).
app/views/spaces/edit.html.erb Whitespace-only change.
app/views/spaces/_form.html.erb Adds private-space toggle and groups autocompleter; includes JS.
app/views/layouts/_user_menu.html.erb Adds “View groups” to admin/curator/owner admin menu.
app/views/groups/index.html.erb New Groups index UI.
app/views/groups/show.html.erb New Groups show page with members + related spaces sections.
app/views/groups/new.html.erb New group page.
app/views/groups/edit.html.erb Edit group page.
app/views/groups/_form.html.erb Group form with user autocompleter + owner flag selection.
app/views/groups/_group.html.erb Group partial.
app/views/groups/_group.json.jbuilder Group JSON representation.
app/views/groups/index.json.jbuilder Groups index JSON.
app/views/groups/show.json.jbuilder Group show JSON.
app/serializers/group_serializer.rb Adds Group serializer for API/JSON:API usage.
app/policies/application_policy.rb Adds shared shown? visibility logic for private-space scoping + docstrings.
app/policies/space_policy.rb Aligns SpacePolicy show/edit/destroy with shown?/admin/space-admin rules.
app/policies/group_policy.rb New GroupPolicy for member/admin visibility and HTML-only writes.
app/policies/material_policy.rb Ensures Material show? also requires shown? (private-space visibility).
app/policies/event_policy.rb Ensures Event show? also requires shown? (private-space visibility).
app/models/user.rb Adds group/group_membership associations + owner helper methods.
app/models/space.rb Adds groups association, private-space validation, and destroy-time handling + docstrings.
app/models/group.rb New Group model with memberships and spaces associations.
app/models/group_membership.rb New join model with owner flag and composite PK intent.
app/models/global_space.rb Adjusts equality semantics for GlobalSpace.
app/helpers/groups_helper.rb Adds Groups helper module.
app/controllers/groups_controller.rb Adds Groups CRUD with membership owner syncing + policy enforcement.
app/controllers/spaces_controller.rb Filters spaces#index by shown? and permits group_ids/is_private.
app/controllers/materials_controller.rb Adds @results_count for index results.
app/controllers/concerns/searchable_index.rb Filters Solr results by shown? and changes JSON:API meta keys.
app/controllers/application_controller.rb Enforces private-space access at space resolution time (host-based).
app/assets/javascripts/templates/autocompleter/group_member.hbs Autocompleter template for group membership rows (incl. owner checkbox).
app/assets/javascripts/templates/autocompleter/group_id.hbs Autocompleter template for group selection in space form.
app/assets/javascripts/show_private_groups.js Toggles required-groups UI based on “private space” checkbox.
app/assets/javascripts/autocompleters.js Adds groups transform function for autocompleter responses.
app/assets/config/manifest.js Includes show_private_groups.js in asset pipeline.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +12 to 16
@spaces = Space.all.select { |space| policy(space).shown? }
respond_to do |format|
format.html
format.json { render json: @groups.as_json(only: [:id, :title]) }
end

@valentinRyckaert valentinRyckaert Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why removing @spaces = Space.all.select { |space| policy(space).shown? }? @spaces needs to be initialized.

Comment on lines +55 to +61
# Override total on the Solr result object so the view gets the right count
@search_results.instance_variable_set(:@total, filtered.length)
def @search_results.total; @total; end

@index_resources = WillPaginate::Collection.create(page, per_page, filtered.length) do |pager|
pager.replace(filtered)
end
Comment thread app/policies/application_policy.rb Outdated
Comment on lines +93 to +97
# Returns:: the default Pundit policy scope for the record's class,
# filtered by #shown?.
def scope
Pundit.policy_scope!(user, record.class)
Pundit.policy_scope!(user, record.class).shown?
end
Comment thread app/models/user.rb
Comment on lines +394 to +396
def is_owner_in_any_group?
Group.all.any? { |group| group.group_memberships.find_by(user: self)&.owner == true }
end
Comment thread app/models/space.rb
Comment on lines +45 to +50
def validate(record)
Rails.logger.info "Validating space: is_private=#{record.is_private}, groups_count=#{record.groups.length}"
if record.is_private && record.groups.length == 0
record.errors.add(:base, "If the space is private, you must add required groups.")
end
end
Comment thread app/views/groups/show.html.erb Outdated
Comment on lines +66 to +70
<%= button_to @group, method: :delete,
class: 'btn btn-danger',
form: { data: { confirm: "Are you sure you want to delete this group?" } } do %>
<i class="glyphicon glyphicon-trash"></i> Delete
<% end %>
@@ -0,0 +1,23 @@
// excuted for space form
Comment on lines +3 to +6
create_join_table :groups, :spaces do |t|
# t.index [:group_id, :space_id]
# t.index [:space_id, :group_id]
end
Comment on lines +1 to +5
class AddIsPrivateToSpaces < ActiveRecord::Migration[7.2]
def change
add_column :spaces, :is_private, :boolean
end
end
Comment on lines 122 to 127
meta: {
facets: facets,
available_facets: available_facets,
query: @search_params,
results_count: total
facets: facets,
:'available-facets' => available_facets,
query: @search_params,
:'results-count' => total
}
@kennethrioja

kennethrioja commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

I just ran the whole test suite on this branch, it works locally.......

Maybe tests fail because of the gem audit failing as well?

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.

3 participants