require_relative 'paged_searcher' require_dependency 'cartodb/errors' module Carto module Api class GroupsController < ::Api::ApplicationController include PagedSearcher ssl_required :index, :show, :create, :update, :destroy, :add_users, :remove_users before_filter :load_fetching_options, only: [:show, :index] before_filter :load_organization before_filter :load_user before_filter :validate_organization_or_user_loaded before_filter :load_group, only: [:show, :update, :destroy, :add_users, :remove_users] before_filter :org_admin_only, only: [:create, :update, :destroy, :add_users, :remove_users] before_filter :org_users_only, only: [:show, :index] before_filter :load_organization_users, only: [:add_users, :remove_users] before_filter :valid_password_confirmation, only: [:destroy, :add_users, :remove_users] rescue_from Carto::ParamInvalidError, with: :rescue_from_carto_error rescue_from Carto::PasswordConfirmationError, with: :rescue_from_password_confirmation_error VALID_ORDER_PARAMS = [:id, :name, :display_name, :organization_id, :updated_at].freeze def index page, per_page, order, _order_direction = page_per_page_order_params(VALID_ORDER_PARAMS) groups = @user ? @user.groups : @organization.groups groups = groups.where('name ilike ?', "%#{params[:q]}%") if params[:q] total_entries = groups.count groups = Carto::PagedModel.paged_association(groups, page, per_page, order) render_jsonp({ groups: groups.map { |g| Carto::Api::GroupPresenter.new(g, @fetching_options).to_poro }, total_entries: total_entries }, 200) end def show render_jsonp(Carto::Api::GroupPresenter.new(@group, @fetching_options).to_poro, 200) end def create @group = @organization.create_group(params['display_name']) render_jsonp(Carto::Api::GroupPresenter.full(@group).to_poro, 200) rescue CartoDB::ModelAlreadyExistsError => e render json: { errors: ["A group with that name already exists"] }, status: 409 rescue ActiveRecord::StatementInvalid => e handle_statement_invalid_error(e, @group) rescue StandardError => e log_error(exception: e) render json: { errors: [e.message] }, status: 500 end def update @group.rename_group_with_extension(params['display_name']) render_jsonp(Carto::Api::GroupPresenter.full(@group).to_poro, 200) rescue CartoDB::ModelAlreadyExistsError => e render json: { errors: ["A group with that name already exists"] }, status: 409 rescue ActiveRecord::StatementInvalid => e handle_statement_invalid_error(e, @group) rescue StandardError => e log_error(exception: e) render json: { errors: [e.message] }, status: 500 end def destroy @group.destroy_group_with_extension render json: {}, status: 204 rescue ActiveRecord::StatementInvalid => e handle_statement_invalid_error(e, @group) rescue StandardError => e log_error(exception: e) render json: { errors: [e.message] }, status: 500 end def add_users @group.add_users_with_extension(@organization_users) render json: {}, status: 200 rescue ActiveRecord::StatementInvalid => e handle_statement_invalid_error(e, @group) rescue StandardError => e log_error(exception: e) render json: { errors: [e.message] }, status: 500 end def remove_users @group.remove_users_with_extension(@organization_users) render json: {}, status: 200 rescue ActiveRecord::StatementInvalid => e handle_statement_invalid_error(e, @group) rescue StandardError => e log_error(exception: e) render json: { errors: [e.message] }, status: 500 end private def load_fetching_options @fetching_options = { fetch_shared_tables_count: params[:fetch_shared_tables_count] == 'true', fetch_shared_maps_count: params[:fetch_shared_maps_count] == 'true', fetch_users: params[:fetch_users] == 'true' } end def load_organization return unless params['organization_id'].present? @organization = Carto::Organization.where(id: params['organization_id']).first render json: { errors: ["Org. #{params['organization_id']} not found"] }, status: 404 unless @organization end def load_user return unless params['user_id'].present? @user = Carto::User.where(id: params['user_id']).first render json: { errors: ["User #{params['user_id']} not found"] }, status: 404 unless @user if @organization.nil? @organization = @user.organization elsif @user.organization_id != @organization.id render json: { errors: ["You can't get other organization users"] }, status: 501 end unless @user.id == current_user.id || current_user.organization_admin? render json: { errors: ["You can't get other users groups"] }, status: 501 end end def validate_organization_or_user_loaded render json: { errors: ["You must set user_id or organization_id"] }, status: 404 unless @organization || @user end def org_users_only unless @organization.id == current_user.organization_id render json: { errors: ["Not organization user"] }, status: 400 end end def org_admin_only unless @organization.admin?(current_user) render json: { errors: ["Not org. admin"] }, status: 400 end end def load_group @group = @organization.groups.where(id: params['group_id']).first render json: { errors: ["Group #{params['group_id']} not found"] }, status: 404 unless @group end def load_organization_users ids = params['users'].present? ? params['users'] : [ params['user_id'] ] @organization_users = ids.map { |id| @organization.users.where(id: id).first } render json: { errors: ["Users #{ids} not found"] }, status: 404 if @organization_users.empty? end def handle_statement_invalid_error(e, group) err_regexp = /ERROR: (.*)\n/ if e.message =~ err_regexp render json: { errors: [err_regexp.match(e.message)[1]] }, status: 422 else log_error(exception: e) render json: { errors: [e.message] }, status: 500 end end def rescue_from_password_confirmation_error(error) log_rescue_from(__method__, error) render_jsonp({ message: "Error modifying groups", errors: [error.message] }, 403) end def log_context super.merge(params: params, group: @group, organization: @organization) end end end end