Skip to content

Commit

Permalink
Update admin credentials to demonstrate best practices for nested mod…
Browse files Browse the repository at this point in the history
…al editing
  • Loading branch information
sfnelson committed Oct 27, 2023
1 parent e03f3ac commit 50603c6
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 37 deletions.
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ GIT
PATH
remote: .
specs:
katalyst-koi (4.2.0.beta.1)
katalyst-koi (4.2.0.beta.2)
bcrypt
importmap-rails
katalyst-content
Expand Down Expand Up @@ -187,7 +187,7 @@ GEM
activesupport (>= 3)
redis (>= 3)
redlock (>= 1.2)
katalyst-kpop (3.0.0.beta.1)
katalyst-kpop (3.0.0.beta.2)
html-attributes-utils
turbo-rails
view_component
Expand Down
35 changes: 23 additions & 12 deletions app/controllers/admin/credentials_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@ module Admin
class CredentialsController < ApplicationController
include Koi::Controller::HasWebauthn

before_action :set_admin_user

layout "kpop"

def new
unless current_admin.webauthn_id
current_admin.update!(webauthn_id: WebAuthn.generate_user_id)
unless @admin_user.webauthn_id
@admin_user.update!(webauthn_id: WebAuthn.generate_user_id)
end

options = webauthn_relying_party.options_for_registration(
user: {
id: current_admin.webauthn_id,
name: current_admin.email,
display_name: current_admin.to_s,
id: @admin_user.webauthn_id,
name: @admin_user.email,
display_name: @admin_user.to_s,
},
exclude: current_admin.credentials.map(&:external_id),
exclude: @admin_user.credentials.map(&:external_id),
)

# Store the newly generated challenge somewhere so you can have it
# for the verification phase.
session[:creation_challenge] = options.challenge

render :new, locals: { options: }
render :new, locals: { admin: @admin_user, options: }
end

def create
Expand All @@ -35,7 +37,7 @@ def create
session.delete(:creation_challenge),
)

credential = current_admin.credentials.find_or_initialize_by(
credential = @admin_user.credentials.find_or_initialize_by(
external_id: webauthn_credential.id,
)

Expand All @@ -46,22 +48,31 @@ def create
)

respond_to do |format|
format.html { redirect_to admin_admin_user_path(current_admin), status: :see_other }
format.turbo_stream { render turbo_stream: turbo_stream.kpop.redirect_to(admin_admin_user_path(current_admin)) }
format.html { redirect_to admin_admin_user_path(@admin_user), status: :see_other }
format.turbo_stream { render locals: { admin: @admin_user } }
end
end

def destroy
credential = current_admin.credentials.find(params[:id])
credential = @admin_user.credentials.find(params[:id])
credential.destroy!

redirect_to admin_admin_user_path(current_admin), status: :see_other
respond_to do |format|
format.html { redirect_to admin_admin_user_path(@admin_user), status: :see_other }
format.turbo_stream { render locals: { admin: @admin_user } }
end
end

private

def credential_params
params.require(:admin_credential).permit(:nickname, :response)
end

def set_admin_user
@admin_user = Admin::User.find(params[:admin_user_id])

head(:forbidden) unless current_admin == @admin_user
end
end
end
15 changes: 0 additions & 15 deletions app/views/admin/admin_users/_authentication.html.erb

This file was deleted.

10 changes: 9 additions & 1 deletion app/views/admin/admin_users/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@
<% end %>
</div>

<%= render("authentication", admin:) %>
<h2>Authentication</h2>

<%= render "admin/credentials/credentials", admin: %>
<% if admin == current_admin %>
<div class="actions-group">
<%= kpop_link_to "Add this device", new_admin_admin_user_credential_path(admin), class: "button button--primary" %>
</div>
<% end %>
7 changes: 7 additions & 0 deletions app/views/admin/credentials/_credentials.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<%= render Katalyst::TableComponent.new(id: dom_id(admin, :credentials), collection: admin.credentials, class: "index-table") do |t, c| %>
<% t.cell :nickname %>
<% t.cell :sign_count %>
<% t.cell :actions, label: "" do %>
<%= button_to "Remove device", admin_admin_user_credential_path(admin, c), method: :delete, class: "button button--text" %>
<% end if admin == current_admin %>
<% end %>
4 changes: 4 additions & 0 deletions app/views/admin/credentials/create.turbo_stream.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= turbo_stream.kpop.dismiss %>
<%= turbo_stream.replace(dom_id(admin, :credentials)) do %>
<%= render "admin/credentials/credentials", admin: %>
<% end %>
3 changes: 3 additions & 0 deletions app/views/admin/credentials/destroy.turbo_stream.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= turbo_stream.replace(dom_id(admin, :credentials)) do %>
<%= render "admin/credentials/credentials", admin: %>
<% end %>
4 changes: 2 additions & 2 deletions app/views/admin/credentials/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<%= render Kpop::ModalComponent.new(title: "Register device") do %>
<%= form_with model: current_admin.credentials.new,
url: admin_admin_user_credentials_path(current_admin),
<%= form_with model: admin.credentials.new,
url: admin_admin_user_credentials_path(admin),
data: {
controller: "webauthn-registration",
action: "submit->webauthn-registration#submit",
Expand Down
2 changes: 1 addition & 1 deletion lib/koi/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Koi
VERSION = "4.2.0.beta.1"
VERSION = "4.2.0.beta.2"
end
16 changes: 12 additions & 4 deletions spec/requests/admin/credentials_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
require "webauthn/fake_client"

RSpec.describe Admin::CredentialsController do
subject { action && response }

let(:admin) { create(:admin) }
let(:client) { WebAuthn::FakeClient.new(origin) }
let(:origin) { "http://www.example.com" }
Expand Down Expand Up @@ -45,9 +47,13 @@

it_behaves_like "requires admin"

it "renders successfully" do
it { is_expected.to kpop_dismiss }

it "returns an update to the credentials table" do
action
expect(response).to kpop_redirect_to(admin_admin_user_path(admin))
html = Nokogiri::HTML.fragment(response.body)
root = Capybara::Node::Simple.new(html)
expect(root).to have_css("turbo-stream[action='replace'][target='credentials_admin_#{admin.id}']")
end

it "creates an admin credential" do
Expand All @@ -69,9 +75,11 @@

it_behaves_like "requires admin"

it "renders successfully" do
it "returns an update to the credentials table" do
action
expect(response).to redirect_to(admin_admin_user_path(admin))
html = Nokogiri::HTML.fragment(response.body)
root = Capybara::Node::Simple.new(html)
expect(root).to have_css("turbo-stream[action='replace'][target='credentials_admin_#{admin.id}']")
end

it "destroys an admin credential" do
Expand Down

0 comments on commit 50603c6

Please sign in to comment.