rest of ip address role model / admin dash work

This commit is contained in:
Dylan Knutson
2025-03-02 09:26:39 +00:00
parent df9c42656c
commit ac50c47865
19 changed files with 653 additions and 12 deletions

View File

@@ -0,0 +1,77 @@
# typed: true
class State::IpAddressRolesController < ApplicationController
before_action :set_ip_address_role, only: %i[edit update destroy]
before_action :authorize_ip_address_roles
# GET /state/ip_address_roles
def index
@ip_address_roles = IpAddressRole.all.order(created_at: :desc)
end
# GET /state/ip_address_roles/new
def new
@ip_address_role = IpAddressRole.new
end
# GET /state/ip_address_roles/1/edit
def edit
end
# POST /state/ip_address_roles
def create
@ip_address_role = IpAddressRole.new(ip_address_role_params)
if @ip_address_role.save
redirect_to state_ip_address_roles_path,
notice: "IP address role was successfully created."
else
render :new
end
end
# PATCH/PUT /state/ip_address_roles/1
def update
if @ip_address_role.update(ip_address_role_params)
redirect_to state_ip_address_roles_path,
notice: "IP address role was successfully updated."
else
render :edit
end
end
# DELETE /state/ip_address_roles/1
def destroy
@ip_address_role.destroy
redirect_to state_ip_address_roles_path,
notice: "IP address role was successfully deleted."
end
private
# Use callbacks to share common setup or constraints between actions
def set_ip_address_role
@ip_address_role = IpAddressRole.find(params[:id])
end
# Only allow a list of trusted parameters through
def ip_address_role_params
params.require(:ip_address_role).permit(
:ip_address,
:role,
:description,
:active,
)
end
# Authorize all actions based on the current action
def authorize_ip_address_roles
case action_name.to_sym
when :index, :new, :edit
authorize IpAddressRole, policy_class: State::IpAddressRolePolicy
when :create
authorize IpAddressRole, policy_class: State::IpAddressRolePolicy
when :update, :destroy
authorize @ip_address_role, policy_class: State::IpAddressRolePolicy
end
end
end

View File

@@ -0,0 +1,35 @@
# typed: strict
module IpAddressHelper
extend T::Sig
# Formats an IPAddr object to display properly with CIDR notation if it's a subnet
# @param ip_addr [IPAddr, nil] The IP address object to format or nil
# @return [String] A formatted string representation of the IP address
sig { params(ip_addr: T.nilable(IPAddr)).returns(String) }
def format_ip_address(ip_addr)
if ip_addr.nil?
""
else
# For IPv4, check if the prefix is not 32 (full mask)
# For IPv6, check if the prefix is not 128 (full mask)
if (ip_addr.ipv4? && ip_addr.prefix < 32) ||
(ip_addr.ipv6? && ip_addr.prefix < 128)
# This is a CIDR range
"#{ip_addr.to_s}/#{ip_addr.prefix}"
else
# Single IP address
ip_addr.to_s
end
end
end
# Determines if the provided IP address is a CIDR range
# @param ip_addr [IPAddr, nil] The IP address to check or nil
# @return [Boolean] true if the address is a CIDR range, false otherwise
sig { params(ip_addr: T.nilable(IPAddr)).returns(T::Boolean) }
def cidr_range?(ip_addr)
return false if ip_addr.nil?
format_ip_address(ip_addr).include?("/")
end
end

View File

@@ -0,0 +1,170 @@
import * as React from 'react';
import { useState, useEffect } from 'react';
interface IpAddressInputProps {
initialValue?: string;
name: string;
id?: string;
placeholder?: string;
onChange?: (value: string, isValid: boolean) => void;
className?: string;
}
type ValidationResult = {
isValid: boolean;
message: string;
type: 'ip' | 'cidr' | 'none';
};
const IpAddressInput: React.FC<IpAddressInputProps> = ({
initialValue = '',
name = 'ip_address_role[ip_address]',
id,
placeholder = 'Example: 192.168.1.1 or 10.0.0.0/24',
onChange,
className = '',
}) => {
const [value, setValue] = useState(initialValue);
const [validation, setValidation] = useState<ValidationResult>({
isValid: true,
message: '',
type: 'none',
});
const [isFocused, setIsFocused] = useState(false);
// Regular expressions for validation
const ipv4Regex =
/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/;
const cidrRegex =
/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}\/(?:[0-9]|[1-2][0-9]|3[0-2])$/;
// Function to validate the input
const validateIpAddress = (input: string): ValidationResult => {
if (!input) {
return {
isValid: false,
message: 'IP address is required',
type: 'none',
};
}
if (input.includes('/')) {
// CIDR notation
if (cidrRegex.test(input)) {
return { isValid: true, message: 'Valid CIDR range', type: 'cidr' };
} else {
return {
isValid: false,
message: 'Invalid CIDR range format',
type: 'cidr',
};
}
} else {
// Single IP address
if (ipv4Regex.test(input)) {
return { isValid: true, message: 'Valid IP address', type: 'ip' };
} else {
return {
isValid: false,
message: 'Invalid IP address format',
type: 'ip',
};
}
}
};
// Update validation when value changes
useEffect(() => {
const result = validateIpAddress(value);
setValidation(result);
// Call onChange callback if provided
if (onChange) {
onChange(value, result.isValid);
}
}, [value, onChange]);
// Get border color based on validation state
const getBorderColorClass = () => {
if (!isFocused) return 'border-slate-300';
if (value === '') return 'border-sky-500';
return validation.isValid ? 'border-emerald-500' : 'border-red-500';
};
return (
<div className="relative">
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
placeholder={placeholder}
className={`block w-full rounded-md shadow-sm focus:ring-sky-500 sm:text-sm ${getBorderColorClass()} ${className}`}
id={id || 'ip_address_input'}
/>
{/* This is a direct input that will be properly included in form submission */}
<input
type="text"
name={name}
value={value}
readOnly
style={{ display: 'none' }}
/>
{/* Validation feedback */}
{value !== '' && (
<div
className={`mt-1 text-sm ${validation.isValid ? 'text-emerald-600' : 'text-red-600'}`}
>
<div className="flex items-center">
{validation.isValid ? (
<svg
className="mr-1 h-4 w-4"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
) : (
<svg
className="mr-1 h-4 w-4"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clipRule="evenodd"
/>
</svg>
)}
<span>{validation.message}</span>
</div>
</div>
)}
{/* Additional helpful information */}
<div className="mt-2 text-sm text-slate-500">
{validation.type === 'cidr' ? (
<span>
CIDR notation represents an IP range (e.g., 10.0.0.0/24 includes 256
addresses)
</span>
) : (
<span>
Enter a single IP address (e.g., 192.168.1.1) or an IP range in CIDR
notation (e.g., 10.0.0.0/24)
</span>
)}
</div>
</div>
);
};
export default IpAddressInput;

View File

@@ -0,0 +1 @@
export { default as IpAddressInput } from './IpAddressInput';

View File

@@ -0,0 +1,8 @@
import * as React from 'react';
import ReactOnRails from 'react-on-rails';
import { IpAddressInput } from '../components';
// This is how react_on_rails can see the component in the browser.
ReactOnRails.register({
IpAddressInput,
});

View File

@@ -5,6 +5,7 @@ import { UserMenu } from '../bundles/Main/components/UserMenu';
import { PostHoverPreviewWrapper } from '../bundles/Main/components/PostHoverPreviewWrapper';
import { UserHoverPreviewWrapper } from '../bundles/Main/components/UserHoverPreviewWrapper';
import { initCollapsibleSections } from '../bundles/UI/collapsibleSections';
import { IpAddressInput } from '../bundles/UI/components';
// This is how react_on_rails can see the components in the browser.
ReactOnRails.register({
@@ -12,6 +13,7 @@ ReactOnRails.register({
UserMenu,
PostHoverPreviewWrapper,
UserHoverPreviewWrapper,
IpAddressInput,
});
// Initialize collapsible sections

View File

@@ -0,0 +1,73 @@
# typed: true
class State::IpAddressRolePolicy < ApplicationPolicy
extend T::Sig
sig { returns(T::Boolean) }
def index?
user_is_admin? || ip_is_admin?
end
sig { returns(T::Boolean) }
def create?
user_is_admin? || ip_is_admin?
end
sig { returns(T::Boolean) }
def new?
create?
end
sig { returns(T::Boolean) }
def update?
user_is_admin? || ip_is_admin?
end
sig { returns(T::Boolean) }
def edit?
update?
end
sig { returns(T::Boolean) }
def destroy?
user_is_admin? || ip_is_admin?
end
private
sig { returns(T::Boolean) }
def user_is_admin?
T.cast(user, T.nilable(User))&.admin? || false
end
sig { returns(T::Boolean) }
def ip_is_admin?
ip_role = @controller.current_ip_address_role
ip_role&.admin? || false
end
class Scope
extend T::Sig
sig do
params(
user: T.nilable(User),
scope: T.untyped,
controller: ApplicationController,
).void
end
def initialize(user, scope, controller)
@user = user
@scope = scope
@controller = controller
end
sig { returns(T.untyped) }
def resolve
if @user&.admin? || @controller.current_ip_address_role&.admin?
@scope
else
@scope.where(id: nil) # Returns empty relation
end
end
end
end

View File

@@ -0,0 +1,81 @@
<%= form_with(model: [:state, ip_address_role], local: true, class: "space-y-6") do |form| %>
<% if ip_address_role.errors.any? %>
<div class="rounded-md bg-red-50 p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">
<%= pluralize(ip_address_role.errors.count, "error") %> prohibited this IP address role from being saved:
</h3>
<div class="mt-2 text-sm text-red-700">
<ul class="list-disc space-y-1 pl-5">
<% ip_address_role.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
<% end %>
<div>
<%= form.label :ip_address, class: "block text-sm font-medium text-slate-700" %>
<div class="mt-1">
<%= react_component("IpAddressInput", {
prerender: false,
props: {
name: "ip_address_role[ip_address]",
id: "ip_address_role_ip_address",
initialValue: format_ip_address(ip_address_role.ip_address),
placeholder: "Example: 192.168.1.1 or 10.0.0.0/24"
}
}) %>
</div>
<% if ip_address_role.errors[:ip_address].any? %>
<p class="mt-2 text-sm text-red-600">
<%= ip_address_role.errors[:ip_address].join(", ") %>
</p>
<% end %>
</div>
<div>
<%= form.label :role, class: "block text-sm font-medium text-slate-700" %>
<div class="mt-1">
<%= form.select :role,
IpAddressRole.roles.keys.map { |r| [r.titleize, r] },
{},
class: "block w-full rounded-md border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 sm:text-sm" %>
</div>
<p class="mt-2 text-sm text-slate-500">Select the role that will be assigned to this IP address or range.</p>
</div>
<div>
<%= form.label :description, class: "block text-sm font-medium text-slate-700" %>
<div class="mt-1">
<%= form.text_area :description,
class: "block w-full rounded-md border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 sm:text-sm",
rows: 3,
placeholder: "Describe why this IP address role is being added" %>
</div>
<p class="mt-2 text-sm text-slate-500">Provide a description for future reference (e.g., "Office network", "Admin home IP").</p>
</div>
<div class="relative flex items-start">
<div class="flex h-5 items-center">
<%= form.check_box :active, class: "h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-500" %>
</div>
<div class="ml-3 text-sm">
<%= form.label :active, class: "font-medium text-slate-700" %>
<p class="text-slate-500">Uncheck to temporarily disable this IP address role without deleting it.</p>
</div>
</div>
<div class="pt-5">
<div class="flex justify-end space-x-3">
<%= link_to 'Cancel',
state_ip_address_roles_path,
class: "rounded-md border border-slate-300 bg-white py-2 px-4 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2" %>
<%= form.submit class: "inline-flex justify-center rounded-md border border-transparent bg-sky-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2" %>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,23 @@
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-2xl font-semibold text-slate-900">Edit IP Address Role</h1>
<p class="mt-2 text-sm text-slate-700">
Modify an existing IP address role for <code class="bg-slate-100 px-1 py-0.5 rounded text-slate-800"><%= @ip_address_role.ip_address %></code>
</p>
</div>
<div class="mt-4 space-x-3 sm:ml-16 sm:mt-0 sm:flex-none">
<%= link_to 'View Details',
state_ip_address_role_path(@ip_address_role),
class: "bg-sky-500 hover:bg-sky-700 text-white font-bold py-2 px-4 rounded" %>
<%= link_to 'Back to List',
state_ip_address_roles_path,
class: "bg-slate-500 hover:bg-slate-700 text-white font-bold py-2 px-4 rounded" %>
</div>
</div>
<div class="mt-6 bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<%= render 'form', ip_address_role: @ip_address_role %>
</div>
</div>
</div>

View File

@@ -0,0 +1,117 @@
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-2xl font-semibold text-slate-900">IP Address Roles</h1>
<p class="mt-2 text-sm text-slate-700">
Manage roles assigned to IP addresses and CIDR ranges.
</p>
</div>
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
<%= link_to 'Add New IP Address Role',
new_state_ip_address_role_path,
class: "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" %>
</div>
</div>
<% if @ip_address_roles.any? %>
<div class="mt-6 overflow-hidden bg-white shadow sm:rounded-lg">
<div class="p-3 sm:p-4">
<table class="min-w-full divide-y divide-slate-300">
<thead>
<tr>
<th class="pb-2 text-left text-sm font-semibold text-slate-900">IP Address/Range</th>
<th class="pb-2 text-left text-sm font-semibold text-slate-900">Role</th>
<th class="pb-2 text-left text-sm font-semibold text-slate-900">Description</th>
<th class="pb-2 text-left text-sm font-semibold text-slate-900">Status</th>
<th class="pb-2 text-left text-sm font-semibold text-slate-900">Created</th>
<th class="relative pb-2"><span class="sr-only">Actions</span></th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200">
<% @ip_address_roles.each do |ip_role| %>
<tr>
<td class="py-2 pr-4 text-sm font-medium text-slate-900">
<code class="bg-slate-100 px-1 py-0.5 rounded text-slate-800"><%= format_ip_address(ip_role.ip_address) %></code>
</td>
<td class="py-2 pr-4 text-sm">
<% pill_color =
case ip_role.role
when "admin"
"bg-rose-100 text-rose-700"
when "moderator"
"bg-amber-100 text-amber-700"
else
"bg-sky-100 text-sky-700"
end
%>
<span class="<%= pill_color %> inline-flex items-center rounded-full px-2.5 py-0.5 font-medium">
<%= ip_role.role.titleize %>
</span>
</td>
<td class="py-2 pr-4 text-sm text-slate-500">
<%= ip_role.description %>
</td>
<td class="py-2 pr-4 text-sm">
<% if ip_role.active? %>
<span class="text-emerald-600 font-medium">Active</span>
<% else %>
<span class="text-slate-500 font-medium">Inactive</span>
<% end %>
</td>
<td class="py-2 pr-4 text-sm text-slate-500">
<%= ip_role.created_at.strftime('%Y-%m-%d %H:%M') %>
</td>
<td class="py-2 text-right text-sm font-medium">
<%= link_to 'Edit',
edit_state_ip_address_role_path(ip_role),
class: "text-blue-600 hover:text-blue-900 mr-3" %>
<%= button_to 'Delete',
state_ip_address_role_path(ip_role),
method: :delete,
class: "text-red-600 hover:text-red-900 inline",
form: {
class: "inline",
data: {
confirm: 'Are you sure you want to delete this IP address role?'
}
} %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<% else %>
<div class="mt-6 rounded-md bg-blue-50 p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-blue-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700">
No IP address roles have been created yet.
</p>
</div>
</div>
</div>
<% end %>
<div class="mt-8 bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium leading-6 text-slate-900">About IP Address Roles</h3>
<div class="mt-2 max-w-xl text-sm text-slate-500">
<p>IP address roles allow you to grant permissions to specific IP addresses or IP ranges without requiring a user account.</p>
</div>
<div class="mt-3">
<h4 class="text-md font-medium text-slate-900">Usage Information</h4>
<ul class="mt-2 list-disc pl-5 text-sm text-slate-700 space-y-1">
<li>Use <strong>single IP addresses</strong> like <code class="bg-slate-100 px-1 py-0.5 rounded">192.168.1.1</code> to grant roles to a specific IP.</li>
<li>Use <strong>CIDR notation</strong> like <code class="bg-slate-100 px-1 py-0.5 rounded">192.168.1.0/24</code> to grant roles to an entire subnet.</li>
<li>Roles will be checked from most specific to least specific, with user accounts taking precedence.</li>
<li>Overlapping IP ranges with different roles are not allowed to prevent conflicts.</li>
</ul>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,20 @@
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-2xl font-semibold text-slate-900">New IP Address Role</h1>
<p class="mt-2 text-sm text-slate-700">
Create a new role for an IP address or CIDR range.
</p>
</div>
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
<%= link_to 'Back to List',
state_ip_address_roles_path,
class: "bg-slate-500 hover:bg-slate-700 text-white font-bold py-2 px-4 rounded" %>
</div>
</div>
<div class="mt-6 bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<%= render 'form', ip_address_role: @ip_address_role %>
</div>
</div>
</div>

View File

@@ -57,19 +57,24 @@ Rails.application.routes.draw do
resources :blobs, controller: :blob_entries, only: [:show], param: :sha256
resources :global_states, path: "state" do
collection do
get "fa-cookies", to: "global_states#fa_cookies"
get "fa-cookies/edit", to: "global_states#edit_fa_cookies"
patch "fa-cookies", to: "global_states#update_fa_cookies"
get "ib-cookies", to: "global_states#ib_cookies"
get "ib-cookies/edit", to: "global_states#edit_ib_cookies"
patch "ib-cookies", to: "global_states#update_ib_cookies"
end
end
authenticate :user, ->(user) { user.admin? } do
# IP address roles management
namespace :state do
resources :ip_address_roles, except: [:show]
end
resources :global_states, path: "state" do
collection do
get "fa-cookies", to: "global_states#fa_cookies"
get "fa-cookies/edit", to: "global_states#edit_fa_cookies"
patch "fa-cookies", to: "global_states#update_fa_cookies"
get "ib-cookies", to: "global_states#ib_cookies"
get "ib-cookies/edit", to: "global_states#edit_ib_cookies"
patch "ib-cookies", to: "global_states#update_ib_cookies"
end
end
mount GoodJob::Engine => "jobs"
mount PgHero::Engine => "pghero"

View File

@@ -46,6 +46,7 @@ class ApplicationController
include ::Domain::PostGroupsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IpAddressHelper
include ::SourceHelper
include ::TimestampHelper
include ::UiHelper

View File

@@ -43,6 +43,7 @@ class DeviseController
include ::Domain::PostGroupsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IpAddressHelper
include ::SourceHelper
include ::TimestampHelper
include ::UiHelper

View File

@@ -39,6 +39,9 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def edit_global_state_path(*args); end
sig { params(args: T.untyped).returns(String) }
def edit_state_ip_address_role_path(*args); end
sig { params(args: T.untyped).returns(String) }
def edit_user_password_path(*args); end
@@ -99,6 +102,9 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def new_rails_conductor_inbound_email_source_path(*args); end
sig { params(args: T.untyped).returns(String) }
def new_state_ip_address_role_path(*args); end
sig { params(args: T.untyped).returns(String) }
def new_user_password_path(*args); end
@@ -198,6 +204,12 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def search_by_name_domain_users_path(*args); end
sig { params(args: T.untyped).returns(String) }
def state_ip_address_role_path(*args); end
sig { params(args: T.untyped).returns(String) }
def state_ip_address_roles_path(*args); end
sig { params(args: T.untyped).returns(String) }
def stats_log_entries_path(*args); end

View File

@@ -39,6 +39,9 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def edit_global_state_url(*args); end
sig { params(args: T.untyped).returns(String) }
def edit_state_ip_address_role_url(*args); end
sig { params(args: T.untyped).returns(String) }
def edit_user_password_url(*args); end
@@ -99,6 +102,9 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def new_rails_conductor_inbound_email_url(*args); end
sig { params(args: T.untyped).returns(String) }
def new_state_ip_address_role_url(*args); end
sig { params(args: T.untyped).returns(String) }
def new_user_password_url(*args); end
@@ -198,6 +204,12 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def search_by_name_domain_users_url(*args); end
sig { params(args: T.untyped).returns(String) }
def state_ip_address_role_url(*args); end
sig { params(args: T.untyped).returns(String) }
def state_ip_address_roles_url(*args); end
sig { params(args: T.untyped).returns(String) }
def stats_log_entries_url(*args); end

View File

@@ -46,6 +46,7 @@ class Rails::ApplicationController
include ::Domain::PostGroupsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IpAddressHelper
include ::SourceHelper
include ::TimestampHelper
include ::UiHelper

View File

@@ -46,6 +46,7 @@ class Rails::Conductor::BaseController
include ::Domain::PostGroupsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IpAddressHelper
include ::SourceHelper
include ::TimestampHelper
include ::UiHelper

View File

@@ -46,6 +46,7 @@ class Rails::HealthController
include ::Domain::PostGroupsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IpAddressHelper
include ::SourceHelper
include ::TimestampHelper
include ::UiHelper