-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CDC #2 - Moving users, projects, and user_projects into core_data_con…
…nector
- Loading branch information
dleadbetter
committed
Sep 10, 2023
1 parent
963d0d0
commit 9b824a3
Showing
22 changed files
with
440 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.2.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
app/controllers/core_data_connector/projects_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module CoreDataConnector | ||
class ProjectsController < ApplicationController | ||
# Search attributes | ||
search_attributes :name | ||
|
||
protected | ||
|
||
# Automatically add the user who created the project as the owner, if they are not an admin. | ||
def after_create(project) | ||
return if current_user.admin? | ||
|
||
UserProject.create( | ||
project_id: project.id, | ||
user_id: current_user.id, | ||
role: UserProject::ROLE_OWNER | ||
) | ||
end | ||
end | ||
end |
29 changes: 29 additions & 0 deletions
29
app/controllers/core_data_connector/user_projects_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module CoreDataConnector | ||
class UserProjectsController < ApplicationController | ||
# Search attributes | ||
search_attributes 'users.name', 'users.email', 'projects.name', 'projects.description' | ||
|
||
# Joins | ||
joins :user, :project | ||
|
||
# Preloads | ||
preloads :user, :project | ||
|
||
protected | ||
|
||
def base_query | ||
return super if params[:id].present? | ||
|
||
query = super | ||
|
||
# User projects are only visible in the context of a user or a project. | ||
if params[:project_id].present? | ||
query.where(project_id: params[:project_id]) | ||
elsif params[:user_id].present? | ||
query.where(user_id: params[:user_id]) | ||
else | ||
query.none | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module CoreDataConnector | ||
class UsersController < ApplicationController | ||
search_attributes :name, :email | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module CoreDataConnector | ||
class Project < ApplicationRecord | ||
# Relationships | ||
has_many :user_projects, dependent: :destroy | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module CoreDataConnector | ||
class User < ApplicationRecord | ||
# Relationships | ||
has_many :user_projects, dependent: :destroy | ||
|
||
# JWT | ||
has_secure_password | ||
|
||
# Validations | ||
validates :email, uniqueness: true | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
module CoreDataConnector | ||
class UserProject < ApplicationRecord | ||
# Constants | ||
ROLE_OWNER = 'owner' | ||
ROLE_EDITOR = 'editor' | ||
ALLOWED_ROLES = [ | ||
ROLE_OWNER, | ||
ROLE_EDITOR | ||
] | ||
|
||
# Relationships | ||
belongs_to :user | ||
belongs_to :project | ||
|
||
# Transient attributes | ||
attr_accessor :name, :email, :password, :password_confirmation | ||
|
||
# Validations | ||
validates :role, inclusion: { in: ALLOWED_ROLES, message: I18n.t('errors.user_project.roles') } | ||
validates :user_id, uniqueness: { scope: :project_id, message: I18n.t('errors.user_project.unique') } | ||
|
||
# Callbacks | ||
before_update :reset_password | ||
before_validation :find_or_create_user, on: :create | ||
|
||
private | ||
|
||
# Create the user record at the same time if the correct attributes are provided and no user_id is set. We'll | ||
# only update the name and password if the user is a new record. | ||
def find_or_create_user | ||
return unless user_id.nil? && name.present? && email.present? && password.present? && password_confirmation.present? | ||
|
||
user = User.find_or_create_by(email: email) do |user| | ||
next unless user.new_record? | ||
|
||
user.assign_attributes( | ||
name: name, | ||
password: password, | ||
password_confirmation: password_confirmation | ||
) | ||
end | ||
|
||
self.user_id = user.id | ||
end | ||
|
||
# Reset the user's password if the password and password confirmation attributes are provided | ||
def reset_password | ||
return unless user_id.present? && password.present? && password_confirmation.present? | ||
|
||
user.update( | ||
password: password, | ||
password_confirmation: password_confirmation | ||
) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
module CoreDataConnector | ||
class ProjectPolicy < BasePolicy | ||
attr_reader :current_user, :project | ||
|
||
def initialize(current_user, project) | ||
@current_user = current_user | ||
@project = project | ||
end | ||
|
||
# Any user can create a new project. | ||
def create? | ||
true | ||
end | ||
|
||
# A user can view any project for which they are a member. | ||
def show? | ||
return true if current_user.admin? | ||
|
||
project_member? | ||
end | ||
|
||
# A user can delete a project if they are an admin or an owner of the project. | ||
def destroy? | ||
return true if current_user.admin? | ||
|
||
project_owner? | ||
end | ||
|
||
# A user can update a project if they are an admin or an owner of the project. | ||
def update? | ||
return true if current_user.admin? | ||
|
||
project_owner? | ||
end | ||
|
||
# Allowed create/update attributes. | ||
def permitted_attributes | ||
[:name, :description] | ||
end | ||
|
||
private | ||
|
||
# Returns a query to find user_projects records for the passed user_id and project_id. | ||
def project_member? | ||
user_projects.exists? | ||
end | ||
|
||
# Returns a query to find user_projects records with an owner role for the passed user_id and project_id. | ||
def project_owner? | ||
user_projects | ||
.where(role: UserProject::ROLE_OWNER) | ||
.exists? | ||
end | ||
|
||
# Returns a query to find user_projects records for the current user and project. | ||
def user_projects | ||
current_user | ||
.user_projects | ||
.where(project_id: project.id) | ||
end | ||
|
||
# Admin users can view all projects. Non-admin users can view projects for which they are members. | ||
class Scope < BaseScope | ||
def resolve | ||
return scope.all if current_user.admin? | ||
|
||
scope.where( | ||
UserProject | ||
.where(user_id: current_user.id) | ||
.where(UserProject.arel_table[:project_id].eq(Project.arel_table[:id])) | ||
.arel | ||
.exists | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
module CoreDataConnector | ||
class UserPolicy < BasePolicy | ||
attr_reader :current_user, :user | ||
|
||
def initialize(current_user, user) | ||
@current_user = current_user | ||
@user = user | ||
end | ||
|
||
# Only admin users can create users directly. | ||
def create? | ||
current_user.admin? | ||
end | ||
|
||
# Only admin users can delete a user. | ||
def destroy? | ||
# Users cannot delete themselves, not even an admin | ||
return false if current_user.id == user.id | ||
|
||
current_user.admin? | ||
end | ||
|
||
# Only admin users can view users outside the context of a project. Users can view themselves outside the | ||
# context of a project. | ||
def show? | ||
return true if current_user.admin? | ||
|
||
current_user.id == user.id | ||
end | ||
|
||
# Only admin users can update users outside the context of a project. Users can update themselves outside the | ||
# context of a project. | ||
def update? | ||
return true if current_user.admin? | ||
|
||
current_user.id == user.id | ||
end | ||
|
||
# Allowed create/update attributes. | ||
def permitted_attributes | ||
params = [:name, :email, :password, :password_confirmation] | ||
params << :admin if current_user.admin? | ||
params | ||
end | ||
|
||
# A user can view another user if they have access to the same project. | ||
class Scope < BaseScope | ||
def resolve | ||
return scope.all if current_user.admin? | ||
|
||
user_projects = UserProject.arel_table.alias('b') | ||
|
||
scope.where( | ||
UserProject | ||
.where(UserProject.arel_table[:user_id].eq(User.arel_table[:id])) | ||
.where( | ||
UserProject | ||
.arel_table | ||
.project(1) | ||
.from(user_projects) | ||
.where(user_projects[:project_id].eq(UserProject.arel_table[:project_id])) | ||
.where(user_projects[:user_id].eq(current_user.id)) | ||
.exists | ||
.to_sql | ||
).arel.exists | ||
) | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.