Определение ролей с помощью Rolify

Я пытаюсь сделать приложение с Rails 4.

Я изучаю управление ролями и хочу использовать Rolify, поскольку он поддерживает назначение ролей на уровне экземпляра.

Для других, которые ищут ту же проблему, ниже есть 2 действительно хороших ответа (я могу отметить только один, но я использовал оба). Ознакомьтесь с ответами lorefnon и Cyb3rDud3 ниже). Я все еще разбираюсь в этом, но сделал миграцию с массивом (как показывает lorefnon) и функциями контроллера/маршрутов (как показывает Cyb3rDud3).

Что меня совершенно сбивает с толку, так это то, что все документы для драгоценного камня Rolify используют консоль для определения ролей.

Как определить роли в моем коде?

Другие участники этого форума задавали вопросы, намекающие на то, что они определяют роли в своем файле db:seeds. Я не хочу этого делать, потому что хочу более жестко контролировать, кто использует мой файл сидов, чем кто может создавать роли.

Где ты это сделал?

Все примеры показывают, что это делается из консоли. Я хочу определить список ролей, а затем я хочу дать ролям разрешения (я хочу использовать pundit для этой части).

У меня есть модель пользователя. Другая жемчужина, на которую я смотрел, была образцом для подражания. Он просит вас создать массив ролей в пользовательской модели. Это настолько очевидно, что вы должны сделать это в Rolify, что ни один из документов не дает вам этого шага?

Где вы определяете роли?


person Mel    schedule 27.11.2015    source источник


Ответы (4)


Что меня совершенно сбивает с толку, так это то, что все документы для драгоценного камня Rolify используют консоль для определения ролей.

Документация Rolify не использует консоль для определения ролей — она демонстрирует, как можно добавлять роли в чистом Ruby. Это удивительно мощно, потому что вы можете определять роли, где бы вы ни запускали ruby. Вы не ограничены статическим списком ролей, определенных в каком-либо файле конфигурации или в какой-либо таблице базы данных.


Первый вопрос, который вам нужно задать, это когда создаются роли?

Наиболее распространенные варианты использования делятся на две группы:

<сильный>1. Роли статичны.

Роли создаются один раз разработчиком приложения, персоналом службы поддержки или руководителями компании во время установки/развертывания приложения, и в течение всего срока службы приложения эти предварительно созданные роли назначаются разным пользователям.

Наиболее распространенные варианты использования таких ролей включают моделирование назначений людей в компании (разработчик, менеджер, служба поддержки и т. д.) или моделирование ранее известных обязанностей (редактор, администратор, наблюдатель и т. д.).

Если ваши роли действительно попадают в такие варианты использования, следующее, что вам нужно решить, — кто отвечает за создание и изменение ролей. Обычно есть две возможности:

1.1. Разработчик приложения — это человек, который должен добавлять/удалять/изменять роли. В таких случаях лучше полагаться на начальные данные или миграции Rails.

Преимущество миграции заключается в том, что вы можете легко откатить данные, если это необходимо. Эта миграция является дополнительной к миграции, созданной генераторами rolify, которые создают для вас схему таблиц, связанных с ролями (см. диаграмму ниже).

Такая миграция может выглядеть так:

БД/мигрировать/20151204083556_create_application_roles.rb

class CreateApplicationRoles < ActiveRecord::Migration
  def up
    ['admin', 'support', 'editor'].each do |role_name|
      Role.create! name: role_name
    end
  end
  def down
    Role.where(name: ['admin', 'support', 'editor']).destroy_all
  end

end

Некоторые люди справедливо считают, что изменение схемы и данных одновременно является антипаттерном. управляемые миграциями. data-migrate — это драгоценный камень, который позволяет вам отделить ориентированные на данные миграции от миграций вашей схемы.

В этом и во всех других случаях, описанных ниже, фактическое назначение ролей будет происходить на основе действий пользователя или событий приложения с помощью add_role или remove_role методов, предоставляемых rolify. Это произойдет в ходе жизненного цикла работающего приложения, а не во время установки приложения.

1.2 Задача добавления/удаления/изменения ролей выполняется службой поддержки или техническими руководителями. В таких случаях потребуется предоставить административный интерфейс для управления ролями.

В этом случае у вас будет контроллер rails для управления ролями. Действие create будет использоваться для создания роли, действие show будет использоваться для представления роли и т. д. Эти действия будут иметь сопровождающие представления, которые предоставят конечному пользователю графический интерфейс для управления ролями.

<сильный>2. Роли являются динамическими

Эта категория охватывает случаи использования, когда роли рассматриваются скорее как категории или теги и могут быть созданы/изменены/удалены конечными пользователями. Например, библиотекарь может назначить определенную роль/категорию определенному жанру книг.

Этот случай похож на 1.2, потому что вы должны обрабатывать создание/удаление/обновление ролей через контроллеры rails.


Следующая часть — это то, как информация структурирована в ваших таблицах.

Rolify ожидает конкретную схему (в определенной степени настраиваемую), но ожидаемая схема достаточно гибкая, чтобы обрабатывать все вышеперечисленные варианты использования.

Рулифицировать таблицы

person lorefnon    schedule 02.12.2015
comment
Привет, а как мне настроить набор ролей, которые можно назначать в моем приложении? Есть ли какое-нибудь руководство, как это сделать? Роли назначаются пользователям некоторыми другими пользователями (у которых есть разрешения экспертов на их назначение). Администраторы создают роли и делегируют полномочия по назначению). Я пытаюсь понять, как создавать роли. Документация по гему показывает, как это сделать в консоли, но я хочу, чтобы они были доступны в моем приложении. не могу понять как это сделать - person Mel; 03.12.2015
comment
Используйте миграцию (данных) для создания ролей. Эти миграции будут написаны на рубине и будут заполнять роли в базе данных. - person lorefnon; 03.12.2015
comment
Вы имеете в виду создать таблицу в базе данных для ролей и иметь роли как логические атрибуты? - person Mel; 03.12.2015
comment
Когда вы запускаете генератор rolify (rails g rolify:role Role User), для вас генерируется миграция для создания таблицы ролей. Я рекомендую создать дополнительную миграцию для заполнения этой таблицы реальными ролями. В Rolify уже есть определенная схема, в которой используется строковое имя столбца для указания имен ролей. Фактические роли представляют собой записи данных в этой таблице, идентифицируемые по имени. - person lorefnon; 03.12.2015
comment
О человек - я не понимаю. Вы имеете в виду определить атрибуты (в виде строк) в этой миграции с именами ролей, которые я хочу использовать в приложении? - person Mel; 05.12.2015
comment
Что значит «на лету»? Я действительно пытаюсь понять, что вы выражаете. Я думаю, что, возможно, rolify предназначен для людей с более глубоким знанием рубина, но я надеюсь использовать его из-за настройки роли, которую я хочу (жемта для подражания недостаточно), хотя одно различие ч / б этих 2 драгоценных камней заключается в том, что образец для подражания показывает как определить роли - person Mel; 07.12.2015
comment
Я обновил вопрос с некоторыми уточнениями. Дайте мне знать, если это поможет. - person lorefnon; 07.12.2015
comment
Лорефнон, спасибо. В моих миграциях нет параметров вверх/вниз (у них есть изменения). Я думаю, вы говорите, что вместо того, чтобы перечислять каждую роль в этой миграции (с логическим триггером для true, если у пользователя есть роль), я создаю метод, подобный вашему, в миграции, и перечисляю в нем массив ролей. Я никогда раньше не использовал миграции таким образом, но я попробую. - person Mel; 08.12.2015

Я буквально только что прошел через тот же процесс, и, как и @user2860931, все, что я смог найти, это несколько примеров того, как назначать роли из консоли. Что мне нужно, так это программно гибкий способ того, как пользователь с ролью администратора или пользователь, скажем, с ролью pmo, может назначать эти роли другим.

Немного поэкспериментировав, я решил это для себя. В этом примере я использую Devise для аутентификации и Rolify для ролей.

Я предполагаю, что у вас уже установлен и работает Devise, поэтому у вас есть существующая модель пользователя. Установите Rolify согласно инструкции на странице гема. Я использовал предложенное имя Роль для модели ролей. Так что делайте все, как указано здесь: https://github.com/RolifyCommunity/rolify. Установите GEM, сгенерируйте с помощью роли пользователя. И перенесите миграцию базы данных.

Это эффективно оставит вас с новой таблицей Roles и отношением has_and_belongs_to_many с таблицей Users.

Что касается моей цели, то мне не требуется обычный интерфейс Create Read (show) Update Delete (CRUD) для ролей, я просто создал несколько с помощью seed.rb вот так.

#Seeding the Role table
#
p "Removing existing #{Role.all.count} roles"
Role.destroy_all
p "Creating 7 roles"
[:user, :admin, :portfolio_manager, :programme_manager,     :project_manager, :coordinator, :pmo].each do |role|
  Role.create( name: role )
end
p "Should have created 7 Roles, roles created: #{Role.all.count}"

Я оставил свой дополнительный комментарий, как и в отношении разработки, чтобы с первого взгляда увидеть, что все работает нормально. Итак, когда вы бежите

грабли дб: семя

У вас будет несколько настроек ролей. В качестве альтернативы вы можете создать контроллер ролей и представления обычным способом, чтобы пользователи с ролью администратора могли добавлять новые роли.

Теперь самое интересное может начаться. До сих пор Devise сделал все, что касается ваших пользователей, или, возможно, вы все равно сделали свой собственный контроллер, но вам нужно создать свой собственный контроллер пользователей и представления. Поскольку мне нужен только список флажков для ролей для каждого пользователя, я сделал это следующим образом. Вот мой

users_controller.rb

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update]

  def index
    @users = User.all
  end

  def show
  end

  def edit
  end

  def update
    respond_to do |format|
      if @user.update(user_params)
        # TODO: Move hardcode flash message into language file
        format.html { redirect_to @user, notice: 'User was successfully updated.'}
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def set_user
    @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:username, :email, {role_ids: []})
  end
end

Теперь вам нужно определить маршруты для них, чтобы они не конфликтовали с маршрутами Devise. На данный момент я сделал это простым и предоставил все обычные маршруты для защиты вашего приложения, которое вы можете изменить, и разрешать маршруты только для тех, которые у вас действительно есть.

routes.rb

Rails.appliction.routes.draw do
  devise_for :users
  root 'pages#home'
  resources :users    #must be after devise
end

Теперь, когда вы делаете rake route, вы получите обычные пути для вашего приложения, чтобы заставить контроллер работать. Вот так и в таком порядке:

                  Prefix Verb   URI Pattern                      Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
             user_unlock POST   /users/unlock(.:format)        devise/unlocks#create
         new_user_unlock GET    /users/unlock/new(.:format)    devise/unlocks#new
                         GET    /users/unlock(.:format)        devise/unlocks#show
                    root GET    /                              pages#home
                   about GET    /about(.:format)               pages#about
                 contact GET    /contact(.:format)             pages#about
                   users GET    /users(.:format)               users#index
                         POST   /users(.:format)               users#create
                new_user GET    /users/new(.:format)           users#new
               edit_user GET    /users/:id/edit(.:format)      users#edit
                    user GET    /users/:id(.:format)           users#show
                         PATCH  /users/:id(.:format)           users#update
                         PUT    /users/:id(.:format)           users#update
                         DELETE /users/:id(.:format)           users#destroy

Все, что осталось сделать сейчас, это создать пользовательский интерфейс, самую важную часть для меня, и, если я правильно понял, для вас тоже. Чтобы построить это быстро, мой пример еще не очень хорошо представлен с полной магией css, но я уверен, что вы можете сделать это по своему вкусу.

Итак, чтобы показать существующих пользователей и выбрать их из списка, создайте index.html.erb в папке /app/views/users. Создайте простой show.html.erb и редактирование, в котором вы можете назначать и удалять существующие роли. Как это.

index.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<% @users.each do |user| %>
  <p>
    <%= link_to "#{user.username}<#{user.email}>", user %>
    <%= link_to "edit", edit_user_path(user) %>
  </p>
<% end %>

show.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<p>
  Username: <%= @user.username %>
</p>
<p>
  Email address: <%= @user.email %>  
</p>

<%= link_to "Back", users_path %>

edit.html.erb

<!-- TODO: Tidy up this file and make it look good -->
<!-- TODO: Remove hard coded text to a locale file -->
<p>
 Username: <%= @user.username %>
</p>
<p>
 Email address: <%= @user.email %>
</p>

<%= form_for @user do |f| %>
  <% Role.all.each do |role| %>
    <%= check_box_tag "user[role_ids][]", role.id, @user.role_ids.include?(role.id) %>
    <%= role.name %></br>
  <% end %>
  <%= f.submit %>
<% end %>

<%= link_to "Back", users_path %>

И вот он у вас есть, простой пользовательский интерфейс, в котором перечислены все доступные роли из базы данных и предусмотрены флажки напротив записи пользователя, чтобы включить или отключить такую ​​роль. Как это:

Пример записи пользователя и списка выбора роли

Для меня это тоже было небольшой головной болью, но, надеюсь, это поможет вам улучшить логику и пользовательский опыт.

person Cyb3rDud3    schedule 05.12.2015
comment
Где вы определили свои роли? Вы помещаете их в файл seed, но есть ли они где-нибудь в вашем коде (например, таблица ролей с логическими атрибутами для YN о том, есть ли у пользователя роль?) - person Mel; 07.12.2015
comment
Я поместил их в файл seed только для того, чтобы их можно было легко загрузить и чтобы они были определены. Как я описал в своем ответе, это обычная таблица, вы даже можете создать ее, если мы хотим добавить новые роли. Код, который я предоставил, перечисляет все роли в таблице ролей и позволит вам назначить их пользователю с помощью флажка. Это видно на приложенном скриншоте. Нет необходимости в других логических значениях, поэтому вы используете гем rolify, чтобы вы могли просто делать такие вещи, как if user.has_role(:admin) then... else - person Cyb3rDud3; 07.12.2015
comment
Привет, теперь я действительно смущен. Как вы помещаете роли в таблицу, если вы не указываете им тип атрибута? Я думаю, что в таблице ролей есть такие поля, как student: boolean, а затем вы делаете это истинным, если пользователь является студентом. Где вы определяете вещи, которые идут в таблице, если не сделано таким образом? - person Mel; 08.12.2015
comment
Вы действительно установили roify? Почему бы быстро не создать новое приложение rails в качестве тестового проекта, и вы сможете его проверить. Вы увидите, что логические значения здесь не используются, это отношение has_and_belongs_to_many. Имя роли хранится в виде строки. Связь поддерживается через внешние ключи. - person Cyb3rDud3; 08.12.2015
comment
Я действительно смущен. Когда вы говорите: «Имя роли не входит в это», я думаю, что мне нужна миграция с чем-то вроде «student:string/boolean» для каждой роли. Я столько раз пытался настроить тестовые приложения. Я не могу понять документы для этого драгоценного камня, чтобы узнать, как начать работу с ним. - person Mel; 08.12.2015
comment
Привет, нет, это не требуется вообще. Когда вы установите гем и запустите его генератор, он создаст для вас миграцию для таблицы. Вы не должны делать свой собственный. Пример, который включил juanm, использует процесс миграции вместо того, где я использую начальный файл. Чисто для внесения в базу "статических" ролей. Любой метод хорош. Итак, у вас есть работающая система аутентификации пользователей, основанная, скажем, на устройствах, и вы создали свой собственный контроллер, чтобы вы могли составлять список пользователей? - person Cyb3rDud3; 08.12.2015
comment
Я даже этого не понимаю. Есть ли туториал для начинающих? - person Mel; 08.12.2015
comment
@ user2860931 Теперь я не понимаю, чтобы установить rolify, см. github.com/RolifyCommunity/rolify. Просто запустите установку драгоценного камня; gem roify запустить установку пакета. Затем создайте свою ролевую модель: rails g rolify Role User. Предполагая, что ваша пользовательская модель называется User. Затем запустите миграцию базы данных: rake db:migrate. Больше ничего нет. Затем вы можете использовать код и шаги в соответствии с исходным ответом. Если вам нужны разные роли, просто измените их в файле seed. Если у вас уже есть модель пользователя, это займет не более 10-20 минут. - person Cyb3rDud3; 08.12.2015
comment
Но я удаляю свой файл семян каждый раз, когда хочу новый тест. Как этот живописный способ получить мой код? Я думал, что миграция должна была создать роли. Если единственный способ определить роли - это семена, это кажется немного шатким, учитывая, что это для заполнения базы данных поддельными данными. - person Mel; 08.12.2015
comment
Вам не обязательно использовать метод файла seed, вы также можете использовать метод миграции в соответствии с ответом @lorefnon. Оба способа абсолютно верны. Кстати, seed.rb предназначен не только для создания поддельных данных. Я бы не стал удалять ваш файл с семенами каждый раз, когда вам нужен новый тест. Я бы разделил их на основе переменных среды, чтобы у вас были семена для разработки, тестирования и производства. Не нужно каждый раз удалять. Но в любом случае это в значительной степени несущественно, поскольку вы можете использовать любой из них или просто создать Scaffold для модели ролей и вводить их через пользовательский интерфейс. Выбор за вами. - person Cyb3rDud3; 08.12.2015
comment
Кстати, если у вас есть свой проект на gitgub, я с удовольствием посмотрю и покажу вам форк с ролями в контексте. Это едва занимает какое-то время.... Удачи. - person Cyb3rDud3; 08.12.2015
comment
Но именно поэтому я спрашиваю о контенте миграции. Я совершенно потерян. Леска создает миграцию. Если я заполню это именами ролей и атрибутами типа данных (либо строковыми, либо логическими - я не понимаю, почему это будет логическое значение), разве это не то, что я только что описал выше? - person Mel; 08.12.2015
comment
В любом случае спасибо, я ценю, что вы пытаетесь помочь - person Mel; 08.12.2015
comment
Да и нет. Миграцию можно использовать для создания таблиц, добавления столбцов и т. д. Но ее также можно использовать для добавления данных. Когда вы запускаете rails g rolify Role User, он создает миграцию, которая создает таблицы и структуры отношений. Вам НЕ нужно делать это снова. Просто запустите это с помощью rake db:migrate, и в этот момент все будет на своих местах. За исключением того, что у вас нет определенных ролей, чтобы создать некоторые из них без создания пользовательского интерфейса, я использую начальный файл, а lorefnon делает это, добавляя еще одну миграцию, предназначенную только для данных. Больше ничего не нужно. - person Cyb3rDud3; 08.12.2015
comment
это заняло у меня почти 6 месяцев, но я возвращаюсь к этому посту каждую неделю, чтобы попытаться понять, как настроить свои роли. Спасибо - это было так полезно. Я очень благодарен за пояснения и примеры в вашем посте. - person Mel; 04.08.2016

Прочитав документацию и примеры на страницах их проектов, я решил не использовать драгоценный камень для управления ролями на моей странице, потому что считал, что настройка и использование займут у меня много времени. Поэтому вместо этого я сделал следующее (я полагаю, что вы использовали разработку для своей пользовательской модели, хотя это и не обязательно):

Если вы хотите, чтобы определенные роли были определены и «статичны», не изменялись, но назначались со вашей страницы, см. ниже, если нет, перейдите к следующей жирной строке

  1. Добавьте поле с именем role: integer в модель User с помощью миграции. (мы используем целое число, потому что это значение будет связано с одной записью в перечислении, которое мы определяем на следующем шаге)
  2. В вашем файле user.rb (модель) добавьте enum, как показано ниже:

    class User < ActiveRecord::Base
      devise :registerable, #...
    
      enum role: [:admin, :normal, :premium, :moreRolesHere ]
      after_initialize :set_default_role, :if => :new_record?
    
      def set_default_role
        self.role ||= :normal
      end
    
    end
    
  3. Затем в любом контроллере, представлении или def вам просто нужно получить текущего пользователя или любого пользователя, которому вы хотите назначить роль, и сделать так же просто, как в строке ниже:

    #let's suppose we want to make premium the current user
    current_user.premium!
    
    #or someone else to be admin
    user = User.first
    user.admin!
    
  4. Затем вы можете сделать свои собственные проверки на любой странице или контроллере, над которым вы работаете, просто спросив роль пользователя:

    #see if the current user is admin
    if current_user.role == "admin"
      #do some admin stuff
    end    
    

Если вы хотите добавлять, изменять и удалять роли на своей странице, а также назначать их там

Прежде всего будьте осторожны при назначении ролей прямо со своей страницы. Проблема здесь в том, что каждый сможет назначить себе свою роль, выбранную в поле. Но если это то, что вам нужно, сделайте следующее:

  1. Создайте модель под названием Role с полем role: string
  2. Создайте контроллер, связанный с этой моделью, roles_controller.rb
  3. Создайте представления, необходимые для отображения действий, связанных с управлением ролями (создание, редактирование, удаление). обратите внимание на то, чтобы эти страницы были доступны только зарегистрированным пользователям. Выбор или добавление роли пользователю, которым вы будете управлять в своем контроллере пользователей или любом другом контроллере, который вы хотите.
  4. В любом другом представлении, в котором вы хотите запросить роль пользователя, вам потребуется получить доступ к таблице ролей и получить те, которые соответствуют пользователю. В таблице пользователей потребуется столбец role_ids: text (это текст, потому что вам нужно сохранить несколько ролей, все роли заключены в массив), который будет представлять его роли. В вашей модели user.rb у вас могут быть методы get_roles и другие методы defs, чтобы иметь более чистый код в контроллерах и представлениях:

    class User < ActiveRecord::Base
      devise :registerable, #...
    
      serialize :role_ids
    
      #will return you an array of roles-(strings) of the user
      def get_roles
        roles = []
        role_ids.each do |role_id|
          roles << Role.find(role_id).role
        end
        return roles      
      end
    
      #ask if this user has some role-(string)
      def has_role(role)
        roles = get_roles
        return roles.include?(role)
      end
    
    end
    
  5. Наконец, конечно, вам нужно будет реализовать контроллер для ролей, создания, обновления и уничтожения, а также всех def, связанных с их представлениями.

Вы можете взглянуть на как сохранить массивы в базе данных, сериализовать< /а>

В этом подходе не используются какие-либо жемчужины, связанные с управлением ролями или авторизацией, как в market: эксперт, канкан, свернуть. Оставьте вам несколько ссылок, если вы скептически относитесь к моему подходу и хотите запачкать руки.

person JuanM.    schedule 30.11.2015

Принятый ответ от @lorefnon действительно содержит порочный анти-шаблон в миграции:

class CreateApplicationRoles < ActiveRecord::Migration
  def up
    ['admin', 'support', 'editor'].each do |role_name|
      Role.create! name: role_name
    end
  end
  def down
    Role.where(name: ['admin', 'support', 'editor']).destroy_all
  end
end

Не из-за принципа разделения данных и схемы, а потому, что мы не должны никогда использовать объект модели при миграции!

Миграции неизменяемы, но модель изменчива: если одна из ваших миграций зависит от одного или нескольких классов модели, ее можно легко сломать, если один из классов будет изменен, переименован или удален (если мы решим, например, удалить изменение наших зависимостей ).

Миграции должны основываться только на механизмах ActiveRecord::Migration (add_column, create_tables, ...) и чистом SQL.

person Jerome Doucet    schedule 09.06.2021