mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 19:48:08 +00:00
feat: Supports masking tokens in super admin (#6491)
Supports masking/unmasking sensitive data such as API Tokens in the super admin dashboard. ref: #6322 Co-authored-by: raph941 <45232708+raph941@users.noreply.github.com> Co-authored-by: phunguyenmurcul <51897872+phunguyenmurcul@users.noreply.github.com>
This commit is contained in:
@@ -2,3 +2,4 @@
|
||||
//= link administrate/application.css
|
||||
//= link administrate/application.js
|
||||
//= link dashboardChart.js
|
||||
//= link secretField.js
|
||||
|
||||
34
app/assets/javascripts/secretField.js
Normal file
34
app/assets/javascripts/secretField.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// eslint-disable-next-line
|
||||
function toggleSecretField(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const toggler = e.currentTarget;
|
||||
const secretField = toggler.parentElement;
|
||||
const textElement = secretField.querySelector('[data-secret-masked]');
|
||||
|
||||
if (!textElement) return;
|
||||
|
||||
if (textElement.dataset.secretMasked === 'false') {
|
||||
textElement.textContent = '•'.repeat(10);
|
||||
textElement.dataset.secretMasked = 'true';
|
||||
toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-show');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
textElement.textContent = secretField.dataset.secretText;
|
||||
textElement.dataset.secretMasked = 'false';
|
||||
toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-hide');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
function copySecretField(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const toggler = e.currentTarget;
|
||||
const secretField = toggler.parentElement;
|
||||
|
||||
navigator.clipboard.writeText(secretField.dataset.secretText);
|
||||
}
|
||||
@@ -43,3 +43,20 @@
|
||||
.cell-label--number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cell-data__secret-field {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 5px;
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class AccessTokenDashboard < Administrate::BaseDashboard
|
||||
ATTRIBUTE_TYPES = {
|
||||
owner: Field::Polymorphic,
|
||||
id: Field::Number,
|
||||
token: Field::String,
|
||||
token: SecretField,
|
||||
created_at: Field::DateTime,
|
||||
updated_at: Field::DateTime
|
||||
}.freeze
|
||||
|
||||
4
app/fields/secret_field.rb
Normal file
4
app/fields/secret_field.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
require 'administrate/field/base'
|
||||
|
||||
class SecretField < Administrate::Field::String
|
||||
end
|
||||
17
app/views/fields/secret_field/_index.html.erb
Normal file
17
app/views/fields/secret_field/_index.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<%#
|
||||
# SecretField Index Partial
|
||||
%>
|
||||
<%= javascript_include_tag "secretField" %>
|
||||
<div data-secret-text="<%= field.data %>" class="cell-data__secret-field">
|
||||
<span data-secret-masked="true">••••••••••</span>
|
||||
<button onclick="toggleSecretField(event)" data-secret-toggler>
|
||||
<svg width="20" height="20">
|
||||
<use xlink:href="#eye-show" />
|
||||
</svg>
|
||||
</button>
|
||||
<button onclick="copySecretField(event)" data-secret-copier>
|
||||
<svg width="20" height="20">
|
||||
<use xlink:href="#icon-copy" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
18
app/views/fields/secret_field/_show.html.erb
Normal file
18
app/views/fields/secret_field/_show.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<%#
|
||||
# SecretField Show Partial
|
||||
%>
|
||||
<%= javascript_include_tag "secretField" %>
|
||||
|
||||
<div data-secret-text="<%= field.data %>" class="cell-data__secret-field">
|
||||
<span data-secret-masked="true">••••••••••</span>
|
||||
<button onclick="toggleSecretField(event)" data-secret-toggler>
|
||||
<svg width="20" height="20">
|
||||
<use xlink:href="#eye-show" />
|
||||
</svg>
|
||||
</button>
|
||||
<button onclick="copySecretField(event)" data-secret-copier>
|
||||
<svg width="20" height="20">
|
||||
<use xlink:href="#icon-copy" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -10,4 +10,16 @@
|
||||
<symbol id="icon-up-caret" viewBox="0 0 48 48">
|
||||
<path d="M2.988 33.02c-1.66 0-1.943-.81-.618-1.824l20-15.28c.878-.672 2.31-.67 3.188 0l20.075 15.288c1.316 1.003 1.048 1.816-.62 1.816H2.987z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-copy" viewBox="0 0 24 24">
|
||||
<path d="M5.503 4.627 5.5 6.75v10.504a3.25 3.25 0 0 0 3.25 3.25h8.616a2.251 2.251 0 0 1-2.122 1.5H8.75A4.75 4.75 0 0 1 4 17.254V6.75c0-.98.627-1.815 1.503-2.123ZM17.75 2A2.25 2.25 0 0 1 20 4.25v13a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-13A2.25 2.25 0 0 1 8.75 2h9Zm0 1.5h-9a.75.75 0 0 0-.75.75v13c0 .414.336.75.75.75h9a.75.75 0 0 0 .75-.75v-13a.75.75 0 0 0-.75-.75Z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="eye-show" viewBox="0 0 24 24">
|
||||
<path d="M12 9.005a4 4 0 1 1 0 8 4 4 0 0 1 0-8Zm0 1.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM12 5.5c4.613 0 8.596 3.15 9.701 7.564a.75.75 0 1 1-1.455.365 8.503 8.503 0 0 0-16.493.004.75.75 0 0 1-1.455-.363A10.003 10.003 0 0 1 12 5.5Z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="eye-hide" viewBox="0 0 24 24">
|
||||
<path d="M2.22 2.22a.75.75 0 0 0-.073.976l.073.084 4.034 4.035a9.986 9.986 0 0 0-3.955 5.75.75.75 0 0 0 1.455.364 8.49 8.49 0 0 1 3.58-5.034l1.81 1.81A4 4 0 0 0 14.8 15.86l5.919 5.92a.75.75 0 0 0 1.133-.977l-.073-.084-6.113-6.114.001-.002-1.2-1.198-2.87-2.87h.002L8.719 7.658l.001-.002-1.133-1.13L3.28 2.22a.75.75 0 0 0-1.06 0Zm7.984 9.045 3.535 3.536a2.5 2.5 0 0 1-3.535-3.535ZM12 5.5c-1 0-1.97.148-2.889.425l1.237 1.236a8.503 8.503 0 0 1 9.899 6.272.75.75 0 0 0 1.455-.363A10.003 10.003 0 0 0 12 5.5Zm.195 3.51 3.801 3.8a4.003 4.003 0 0 0-3.801-3.8Z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user