Squashed commit of the following:

commit 42f6ef437a4054f2aed6467d8c85a8c1ec7f03c2
Author: Lawrence Hook <lawrencehook@gmail.com>
Date:   Sun Dec 15 12:12:01 2024 -0500

    Implement password removal. Fix button styling

commit 6503b2ac8460b148a17e42015a732b248048fa40
Author: Lawrence Hook <lawrencehook@gmail.com>
Date:   Sun Dec 15 11:38:32 2024 -0500

    Convert to password attributes to snake_case and add them to import/export.

commit 5f6f0e95e693a3ba8c2c59dc7311e30cd9af096d
Merge: 06edb5a e9b2af8
Author: Lawrence Hook <lawrencehook@gmail.com>
Date:   Sat Dec 14 19:27:49 2024 -0500

    Merge branch 'main' into feature/password

commit 06edb5a13151e6228723ecae409c75fea74ede37
Author: Lawrence Hook <lawrencehook@gmail.com>
Date:   Sat Dec 14 19:26:54 2024 -0500

    push progress on password feature
This commit is contained in:
Lawrence Hook
2024-12-15 12:13:07 -05:00
parent e9b2af8f87
commit 5b47e2e636
6 changed files with 459 additions and 15 deletions
+117 -2
View File
@@ -57,7 +57,7 @@ html[foo="bar"] {
margin-right: auto;
padding: 5px 20px;
z-index: 11;
z-index: 110;
color: var(--body-background-color);
background-color: var(--body-color);
@@ -275,6 +275,121 @@ html[schedule="false"] #schedule_container > *:not(#schedule_modal_header) {
cursor: pointer;
}
/* Password lock screen */
html:not([password='true']) #password_lock_container,
html[passwordentered='true'] #password_lock_container,
html[foo=bar] {
display: none;
}
#password_lock_container {
position: fixed;
top: 0;
left: 0;
bottom: calc(var(--footer-height) + 3px);
width: 100vw;
z-index: 100;
font-size: 1rem;
background-color: var(--body-background-color);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 15px;
}
#password_lock_container button {
color: black;
}
/* Password Modal */
#password_container_background {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 100;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
}
#password_container_background button {
color: black;
}
#enable_password_container, #enable_password_container * {
color: var(--body-color);
}
#enable_password_container, #disable_password_container {
position: relative;
height: fit-content;
width: min(90vw, 450px);
margin-top: calc(1.2 * var(--header-height));
margin-right: auto;
margin-left: auto;
padding: 20px 10px;
text-align: center;
background-color: var(--body-background-color);
box-shadow: 0px 0px .5px .5px var(--box-shadow-color);
box-sizing: border-box;
border: 1px solid var(--box-shadow-color);
border-radius: 2px;
display: flex;
gap: 20px;
flex-direction: column;
align-items: center;
}
#password_modal_header {
display: flex;
gap: 15px;
flex-direction: column;
align-items: center;
}
#password_modal_header div:first-child {
font-size: 1.1rem;
font-weight: bold;
}
#enable-password {
display: flex;
flex-direction: row;
gap: 10px;
}
#password-entries {
font-size: 0.85rem;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-gap: 10px;
}
#password-entries > * {
justify-self: start;
}
#password-button button {
color: var(--body-background-color);
}
#disable_password_container a:first-child {
font-weight: bold;
}
/* Menu Timer */
#timer_container {
position: fixed;
@@ -285,7 +400,7 @@ html[schedule="false"] #schedule_container > *:not(#schedule_modal_header) {
z-index: 100;
font-size: 1rem;
background-color: rgba(255, 255, 255, 0.97);
background-color: var(--body-background-color);
display: flex;
flex-direction: column;
+34
View File
@@ -15,6 +15,7 @@
<link rel="stylesheet" type="text/css" href="/options/body.css">
<link rel="stylesheet" type="text/css" href="/options/footer.css">
<script defer src="/shared/md5.js"></script>
<script defer src="/shared/main.js"></script>
<script defer src="/shared/https.js"></script>
<script defer src="/shared/analytics.js"></script>
@@ -93,6 +94,32 @@
</div>
</div>
<div id="password_container_background" hidden>
<div id="enable_password_container" hidden>
<div id='password_modal_header'>
<div>Lock RYS settings with a password.</div>
</div>
<div id='password-entries'>
<a>Enter Password</a>
<input id="password-input-1" type='password'>
<a>Re-enter Password</a>
<input id="password-input-2" type='password'>
</div>
<div id='password-button'><button disabled>Confirm</button></div>
</div>
<div id='disable_password_container' hidden>
<a>Password protection is enabled.</a>
<a>To disable, re-enter below.</a>
<input id='disable-password-input' type='password'>
<button id='disable-password-button'>Remove Password</button>
</div>
</div>
<div id="timer_container">
<div>Menu Timer</div>
<div></div>
@@ -105,6 +132,7 @@
</div>
<div id='settings-menu' class='hidden'>
<div id='settings-password'>Password</div>
<div id='settings-schedule'>Schedule</div>
<hr>
<div id='import-settings'>Import settings</div>
@@ -196,6 +224,12 @@
<div id="disabled_message_open_schedule">Edit schedule</div>
</div>
<div id="password_lock_container">
<a>Enter Password.</a>
<input id='unlock-password-input' type="password">
<button id='unlock-password-button'>Confirm</button>
</div>
<div id="main_container">
<div id="sidebar">
+112 -11
View File
@@ -35,17 +35,17 @@ POWER_OPTIONS.forEach(o => {
/**************
* Scheduling *
**************/
const scheduleModalContainer = document.getElementById('schedule_container_background');
const SCHEDULING_OPTION = document.getElementById('settings-schedule');
const OPEN_SCHEDULE_OPTION = qs('#open-schedule');
const RESUME_SCHEDULE_OPTION = qs('#resume-schedule');
const scheduleToggleContainer = document.getElementById('enable-schedule');
const scheduleToggle = scheduleToggleContainer.querySelector('svg');
const SCHEDULE_TIMES = document.getElementById('schedule-times');
const SCHEDULE_DAYS = document.getElementById('schedule-days');
const SCHEDULE_DAYS_OPTIONS = qsa('div', SCHEDULE_DAYS);
const TIME_PRESETS = qsa('#times-container .predefined-options a');
const DAY_PRESETS = qsa('#days-container .predefined-options a');
const scheduleModalContainer = qs('#schedule_container_background');
const SCHEDULING_OPTION = qs('#settings-schedule');
const OPEN_SCHEDULE_OPTION = qs('#open-schedule');
const RESUME_SCHEDULE_OPTION = qs('#resume-schedule');
const scheduleToggleContainer = qs('#enable-schedule');
const scheduleToggle = qs('svg', scheduleToggleContainer);
const SCHEDULE_TIMES = qs('#schedule-times');
const SCHEDULE_DAYS = qs('#schedule-days');
const SCHEDULE_DAYS_OPTIONS = qsa('div', SCHEDULE_DAYS);
const TIME_PRESETS = qsa('#times-container .predefined-options a');
const DAY_PRESETS = qsa('#days-container .predefined-options a');
function updateTimes(times) {
SCHEDULE_TIMES.value = times;
@@ -147,6 +147,107 @@ DAY_PRESETS.forEach(node => {
});
});
/************
* Password *
************/
const passwordModalContainer = qs('#password_container_background');
const ENABLE_PASSWORD_CONTAINER = qs('#enable_password_container');
const PASSWORD_OPTION = qs('#settings-password');
const PASSWORD_INPUT1 = qs('#password-input-1');
const PASSWORD_INPUT2 = qs('#password-input-2');
const PASSWORD_BUTTON = qs('#password-button button');
const UNLOCK_PASSWORD_INPUT = qs('#unlock-password-input');
const UNLOCK_PASSWORD_BUTTON = qs('#unlock-password-button');
const DISABLE_PASSWORD_CONTAINER = qs('#disable_password_container');
const DISABLE_PASSWORD_INPUT = qs('#disable-password-input');
const DISABLE_PASSWORD_BUTTON = qs('#disable-password-button');
function closePasswordModal() {
passwordModalContainer.toggleAttribute('hidden', true);
}
passwordModalContainer.addEventListener('click', e => {
if (e.target !== passwordModalContainer) return;
closePasswordModal();
});
function openPasswordModal() {
passwordModalContainer.toggleAttribute('hidden', false);
PASSWORD_INPUT1.value = '';
PASSWORD_INPUT2.value = '';
// Get local data.
browser.storage.local.get(PASSWORD_SETTINGS, settings => {
const { password, hashed_password } = settings;
ENABLE_PASSWORD_CONTAINER.toggleAttribute('hidden', password);
DISABLE_PASSWORD_CONTAINER.toggleAttribute('hidden', !password);
});
}
PASSWORD_OPTION.addEventListener('click', e => {
openPasswordModal();
});
function lockWithPassword() {
HTML.removeAttribute('passwordEntered');
}
function unlockWithPassword() {
HTML.setAttribute('passwordEntered', 'true');
}
PASSWORD_INPUT2.addEventListener('input', e => {
const password = PASSWORD_INPUT1.value;
const reentry = PASSWORD_INPUT2.value;
const matching = password !== '' && password === reentry;
PASSWORD_BUTTON.toggleAttribute('disabled', !matching);
});
PASSWORD_BUTTON.addEventListener('click', e => {
const password = PASSWORD_INPUT1.value;
const reentry = PASSWORD_INPUT2.value;
const matching = password !== '' && password === reentry;
if (!matching) return;
// Set password.
const hashed = md5(password);
updateSetting('password', true, { manual: true });
updateSetting('hashed_password', hashed, { manual: true });
lockWithPassword();
closePasswordModal();
});
UNLOCK_PASSWORD_BUTTON.addEventListener('click', e => {
const hashedInput = md5(UNLOCK_PASSWORD_INPUT.value);
const hashedPassword = HTML.getAttribute('hashed_password');
const matching = hashedInput === hashedPassword;
if (!matching) {
displayStatus('Wrong password');
} else {
displayStatus('Unlocked');
unlockWithPassword();
}
});
DISABLE_PASSWORD_BUTTON.addEventListener('click', e => {
const password = DISABLE_PASSWORD_INPUT.value;
const hashedInput = md5(password);
const hashedPassword = HTML.getAttribute('hashed_password');
const matching = hashedInput === hashedPassword;
if (!matching) {
displayStatus('Wrong password');
return;
}
// Disable password settings.
updateSetting('password', false, { manual: true });
updateSetting('hashed_password', '', { manual: true });
closePasswordModal();
displayStatus('Password disabled');
});
// Logging toggle
const LOG_ENABLE = document.getElementById('log-enable');
LOG_ENABLE.addEventListener('click', e => {
+8
View File
@@ -594,12 +594,18 @@ const SCHEDULE_SETTINGS = {
scheduleDays: 'mo,tu,we,th,fr',
};
const PASSWORD_SETTINGS = {
"password": false,
"hashed_password": '',
}
const OTHER_SETTINGS = {
global_enable: true,
dark_mode: false,
log_enabled: true,
...TIMED_SETTINGS,
...SCHEDULE_SETTINGS,
...PASSWORD_SETTINGS,
};
const DEFAULT_SETTINGS = SECTIONS.reduce((acc, fieldset) => {
@@ -695,6 +701,8 @@ const idToShortId = {
"lock_code": '82',
"remove_clip_button": '83',
"shrink_video_thumbnails": '84',
"password": '85',
"hashed_password": '86',
};
+186
View File
@@ -0,0 +1,186 @@
/* Source: http://www.myersdaily.org/joseph/javascript/md5.js */
function md5cycle(x, k) {
var a = x[0], b = x[1], c = x[2], d = x[3];
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
a = ff(a, b, c, d, k[4], 7, -176418897);
d = ff(d, a, b, c, k[5], 12, 1200080426);
c = ff(c, d, a, b, k[6], 17, -1473231341);
b = ff(b, c, d, a, k[7], 22, -45705983);
a = ff(a, b, c, d, k[8], 7, 1770035416);
d = ff(d, a, b, c, k[9], 12, -1958414417);
c = ff(c, d, a, b, k[10], 17, -42063);
b = ff(b, c, d, a, k[11], 22, -1990404162);
a = ff(a, b, c, d, k[12], 7, 1804603682);
d = ff(d, a, b, c, k[13], 12, -40341101);
c = ff(c, d, a, b, k[14], 17, -1502002290);
b = ff(b, c, d, a, k[15], 22, 1236535329);
a = gg(a, b, c, d, k[1], 5, -165796510);
d = gg(d, a, b, c, k[6], 9, -1069501632);
c = gg(c, d, a, b, k[11], 14, 643717713);
b = gg(b, c, d, a, k[0], 20, -373897302);
a = gg(a, b, c, d, k[5], 5, -701558691);
d = gg(d, a, b, c, k[10], 9, 38016083);
c = gg(c, d, a, b, k[15], 14, -660478335);
b = gg(b, c, d, a, k[4], 20, -405537848);
a = gg(a, b, c, d, k[9], 5, 568446438);
d = gg(d, a, b, c, k[14], 9, -1019803690);
c = gg(c, d, a, b, k[3], 14, -187363961);
b = gg(b, c, d, a, k[8], 20, 1163531501);
a = gg(a, b, c, d, k[13], 5, -1444681467);
d = gg(d, a, b, c, k[2], 9, -51403784);
c = gg(c, d, a, b, k[7], 14, 1735328473);
b = gg(b, c, d, a, k[12], 20, -1926607734);
a = hh(a, b, c, d, k[5], 4, -378558);
d = hh(d, a, b, c, k[8], 11, -2022574463);
c = hh(c, d, a, b, k[11], 16, 1839030562);
b = hh(b, c, d, a, k[14], 23, -35309556);
a = hh(a, b, c, d, k[1], 4, -1530992060);
d = hh(d, a, b, c, k[4], 11, 1272893353);
c = hh(c, d, a, b, k[7], 16, -155497632);
b = hh(b, c, d, a, k[10], 23, -1094730640);
a = hh(a, b, c, d, k[13], 4, 681279174);
d = hh(d, a, b, c, k[0], 11, -358537222);
c = hh(c, d, a, b, k[3], 16, -722521979);
b = hh(b, c, d, a, k[6], 23, 76029189);
a = hh(a, b, c, d, k[9], 4, -640364487);
d = hh(d, a, b, c, k[12], 11, -421815835);
c = hh(c, d, a, b, k[15], 16, 530742520);
b = hh(b, c, d, a, k[2], 23, -995338651);
a = ii(a, b, c, d, k[0], 6, -198630844);
d = ii(d, a, b, c, k[7], 10, 1126891415);
c = ii(c, d, a, b, k[14], 15, -1416354905);
b = ii(b, c, d, a, k[5], 21, -57434055);
a = ii(a, b, c, d, k[12], 6, 1700485571);
d = ii(d, a, b, c, k[3], 10, -1894986606);
c = ii(c, d, a, b, k[10], 15, -1051523);
b = ii(b, c, d, a, k[1], 21, -2054922799);
a = ii(a, b, c, d, k[8], 6, 1873313359);
d = ii(d, a, b, c, k[15], 10, -30611744);
c = ii(c, d, a, b, k[6], 15, -1560198380);
b = ii(b, c, d, a, k[13], 21, 1309151649);
a = ii(a, b, c, d, k[4], 6, -145523070);
d = ii(d, a, b, c, k[11], 10, -1120210379);
c = ii(c, d, a, b, k[2], 15, 718787259);
b = ii(b, c, d, a, k[9], 21, -343485551);
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t);
}
function md51(s) {
txt = '';
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878], i;
for (i=64; i<=s.length; i+=64) {
md5cycle(state, md5blk(s.substring(i-64, i)));
}
s = s.substring(i-64);
var tail = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
for (i=0; i<s.length; i++)
tail[i>>2] |= s.charCodeAt(i) << ((i%4) << 3);
tail[i>>2] |= 0x80 << ((i%4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i=0; i<16; i++) tail[i] = 0;
}
tail[14] = n*8;
md5cycle(state, tail);
return state;
}
/* there needs to be support for Unicode here,
* unless we pretend that we can redefine the MD-5
* algorithm for multi-byte characters (perhaps
* by adding every four 16-bit characters and
* shortening the sum to 32 bits). Otherwise
* I suggest performing MD-5 as if every character
* was two bytes--e.g., 0040 0025 = @%--but then
* how will an ordinary MD-5 sum be matched?
* There is no way to standardize text to something
* like UTF-8 before transformation; speed cost is
* utterly prohibitive. The JavaScript standard
* itself needs to look at this: it should start
* providing access to strings as preformed UTF-8
* 8-bit unsigned value arrays.
*/
function md5blk(s) { /* I figured global was faster. */
var md5blks = [], i; /* Andy King said do it this way. */
for (i=0; i<64; i+=4) {
md5blks[i>>2] = s.charCodeAt(i)
+ (s.charCodeAt(i+1) << 8)
+ (s.charCodeAt(i+2) << 16)
+ (s.charCodeAt(i+3) << 24);
}
return md5blks;
}
var hex_chr = '0123456789abcdef'.split('');
function rhex(n)
{
var s='', j=0;
for(; j<4; j++)
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F]
+ hex_chr[(n >> (j * 8)) & 0x0F];
return s;
}
function hex(x) {
for (var i=0; i<x.length; i++)
x[i] = rhex(x[i]);
return x.join('');
}
function md5(s) {
return hex(md51(s));
}
/* this function is much faster,
so if possible we use it. Some IEs
are the only ones I know of that
need the idiotic second function,
generated by an if clause. */
function add32(a, b) {
return (a + b) & 0xFFFFFFFF;
}
if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
function add32(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
}
+2 -2
View File
@@ -118,8 +118,8 @@ function parseTimeRemaining(ms) {
function hydrateDropdown(
root,
dropdown,
show = d => d.removeAttribute('hidden'),
hide = d => d.setAttribute('hidden', '')
show = d => d.toggleAttribute('hidden', false),
hide = d => d.toggleAttribute('hidden', true)
) {
function clickListener(e) {