Add advanced timeout and password features to kiosk
New Features: - Time-based session lockout: Automatically lock at a specific time daily - Active hours for lockout: Only enforce timeout during configured hours - Password on boot: Require password when system powers on/reboots - Enhanced lockout timeout: Now respects active hours configuration Configuration Options: - lockoutAtTime: Time to auto-lock (e.g., "17:00") - lockoutActiveStart/End: Time range for lockout enforcement - requirePasswordOnBoot: Boolean to require password on system boot Technical Changes: - Updated configure_password_protection() with new prompts - Added lockout time validation (HH:MM format) - Enhanced main.js with isWithinActiveHours() and checkScheduledLockTime() - Added boot flag creation in openbox autostart - Updated config.json schema with new fields The session timeout (inactivityTimeout) continues to work as before, returning to home page after inactivity. The lockout timeout provides an additional security layer with password protection.
This commit is contained in:
+147
-7
@@ -140,6 +140,10 @@ ENABLE_KEYBOARD_BUTTON="true"
|
|||||||
ENABLE_PASSWORD_PROTECTION="false"
|
ENABLE_PASSWORD_PROTECTION="false"
|
||||||
LOCKOUT_PASSWORD=""
|
LOCKOUT_PASSWORD=""
|
||||||
LOCKOUT_TIMEOUT=0
|
LOCKOUT_TIMEOUT=0
|
||||||
|
LOCKOUT_AT_TIME=""
|
||||||
|
LOCKOUT_ACTIVE_START=""
|
||||||
|
LOCKOUT_ACTIVE_END=""
|
||||||
|
REQUIRE_PASSWORD_ON_BOOT="false"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### SECTION 2: HELPER/UTILITY FUNCTIONS
|
### SECTION 2: HELPER/UTILITY FUNCTIONS
|
||||||
@@ -954,6 +958,9 @@ configure_password_protection() {
|
|||||||
echo " • Blank screen after inactivity period"
|
echo " • Blank screen after inactivity period"
|
||||||
echo " • Password required to unlock"
|
echo " • Password required to unlock"
|
||||||
echo " • Password required after display schedule wake-up"
|
echo " • Password required after display schedule wake-up"
|
||||||
|
echo " • Optional: Lock at specific time"
|
||||||
|
echo " • Optional: Only active during certain hours"
|
||||||
|
echo " • Optional: Require password on system boot"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Load existing config if available
|
# Load existing config if available
|
||||||
@@ -997,14 +1004,62 @@ configure_password_protection() {
|
|||||||
# Ask for lockout timeout
|
# Ask for lockout timeout
|
||||||
echo
|
echo
|
||||||
echo "Session lockout time (minutes of inactivity):"
|
echo "Session lockout time (minutes of inactivity):"
|
||||||
echo " Enter 0 to only require password after display schedule wake-up"
|
echo " Enter 0 to only require password after display schedule wake-up or boot"
|
||||||
LOCKOUT_TIMEOUT=$(ask_integer "Lockout timeout in minutes" "30" 0 1440)
|
LOCKOUT_TIMEOUT=$(ask_integer "Lockout timeout in minutes" "30" 0 1440)
|
||||||
|
|
||||||
|
# Ask for time-based lockout
|
||||||
|
echo
|
||||||
|
if ask_yes_no "Lock automatically at a specific time each day?" "n"; then
|
||||||
|
read -r -p "Enter time to lock (HH:MM, 24-hour format, e.g., 17:00): " LOCKOUT_AT_TIME
|
||||||
|
# Validate time format
|
||||||
|
if [[ "$LOCKOUT_AT_TIME" =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then
|
||||||
|
log_success "Will lock at ${LOCKOUT_AT_TIME} daily"
|
||||||
|
else
|
||||||
|
echo "⚠ Invalid time format, time-based lockout disabled"
|
||||||
|
LOCKOUT_AT_TIME=""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
LOCKOUT_AT_TIME=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ask for active hours
|
||||||
|
echo
|
||||||
|
if ask_yes_no "Only enforce lockout during specific hours?" "n"; then
|
||||||
|
read -r -p "Enter start time (HH:MM, e.g., 09:00): " LOCKOUT_ACTIVE_START
|
||||||
|
read -r -p "Enter end time (HH:MM, e.g., 17:00): " LOCKOUT_ACTIVE_END
|
||||||
|
# Validate time formats
|
||||||
|
if [[ "$LOCKOUT_ACTIVE_START" =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]] && \
|
||||||
|
[[ "$LOCKOUT_ACTIVE_END" =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then
|
||||||
|
log_success "Lockout active ${LOCKOUT_ACTIVE_START} to ${LOCKOUT_ACTIVE_END}"
|
||||||
|
else
|
||||||
|
echo "⚠ Invalid time format, active hours disabled"
|
||||||
|
LOCKOUT_ACTIVE_START=""
|
||||||
|
LOCKOUT_ACTIVE_END=""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
LOCKOUT_ACTIVE_START=""
|
||||||
|
LOCKOUT_ACTIVE_END=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ask for boot password requirement
|
||||||
|
echo
|
||||||
|
if ask_yes_no "Require password on system boot/power on?" "y"; then
|
||||||
|
REQUIRE_PASSWORD_ON_BOOT="true"
|
||||||
|
log_success "Password will be required on boot"
|
||||||
|
else
|
||||||
|
REQUIRE_PASSWORD_ON_BOOT="false"
|
||||||
|
log_info "Password only required after inactivity or display wake"
|
||||||
|
fi
|
||||||
|
|
||||||
log_success "Password protection enabled (lockout: ${LOCKOUT_TIMEOUT} min)"
|
log_success "Password protection enabled (lockout: ${LOCKOUT_TIMEOUT} min)"
|
||||||
else
|
else
|
||||||
ENABLE_PASSWORD_PROTECTION="false"
|
ENABLE_PASSWORD_PROTECTION="false"
|
||||||
LOCKOUT_PASSWORD=""
|
LOCKOUT_PASSWORD=""
|
||||||
LOCKOUT_TIMEOUT=0
|
LOCKOUT_TIMEOUT=0
|
||||||
|
LOCKOUT_AT_TIME=""
|
||||||
|
LOCKOUT_ACTIVE_START=""
|
||||||
|
LOCKOUT_ACTIVE_END=""
|
||||||
|
REQUIRE_PASSWORD_ON_BOOT="false"
|
||||||
log_info "Password protection disabled"
|
log_info "Password protection disabled"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -3148,6 +3203,12 @@ load_config() {
|
|||||||
|
|
||||||
LOCKOUT_PASSWORD=$(sudo -u "$KIOSK_USER" jq -r '.lockoutPassword // ""' "$CONFIG_PATH" 2>/dev/null)
|
LOCKOUT_PASSWORD=$(sudo -u "$KIOSK_USER" jq -r '.lockoutPassword // ""' "$CONFIG_PATH" 2>/dev/null)
|
||||||
LOCKOUT_TIMEOUT=$(sudo -u "$KIOSK_USER" jq -r '.lockoutTimeout // 0' "$CONFIG_PATH" 2>/dev/null)
|
LOCKOUT_TIMEOUT=$(sudo -u "$KIOSK_USER" jq -r '.lockoutTimeout // 0' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
LOCKOUT_AT_TIME=$(sudo -u "$KIOSK_USER" jq -r '.lockoutAtTime // ""' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
LOCKOUT_ACTIVE_START=$(sudo -u "$KIOSK_USER" jq -r '.lockoutActiveStart // ""' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
LOCKOUT_ACTIVE_END=$(sudo -u "$KIOSK_USER" jq -r '.lockoutActiveEnd // ""' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
|
||||||
|
local loaded_boot_password=$(sudo -u "$KIOSK_USER" jq -r '.requirePasswordOnBoot // false' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
[[ "$loaded_boot_password" == "true" ]] && REQUIRE_PASSWORD_ON_BOOT="true" || REQUIRE_PASSWORD_ON_BOOT="false"
|
||||||
|
|
||||||
local tab_count=$(sudo -u "$KIOSK_USER" jq -r '.tabs | length' "$CONFIG_PATH" 2>/dev/null || echo "0")
|
local tab_count=$(sudo -u "$KIOSK_USER" jq -r '.tabs | length' "$CONFIG_PATH" 2>/dev/null || echo "0")
|
||||||
URLS=()
|
URLS=()
|
||||||
@@ -3192,6 +3253,9 @@ save_config() {
|
|||||||
local password_json="false"
|
local password_json="false"
|
||||||
[[ "$ENABLE_PASSWORD_PROTECTION" == "true" ]] && password_json="true"
|
[[ "$ENABLE_PASSWORD_PROTECTION" == "true" ]] && password_json="true"
|
||||||
|
|
||||||
|
local boot_password_json="false"
|
||||||
|
[[ "$REQUIRE_PASSWORD_ON_BOOT" == "true" ]] && boot_password_json="true"
|
||||||
|
|
||||||
jq -n \
|
jq -n \
|
||||||
--arg unit "s" \
|
--arg unit "s" \
|
||||||
--argjson autoswitch "$auto_json" \
|
--argjson autoswitch "$auto_json" \
|
||||||
@@ -3206,7 +3270,11 @@ save_config() {
|
|||||||
--argjson enablePasswordProtection "$password_json" \
|
--argjson enablePasswordProtection "$password_json" \
|
||||||
--arg lockoutPassword "${LOCKOUT_PASSWORD:-}" \
|
--arg lockoutPassword "${LOCKOUT_PASSWORD:-}" \
|
||||||
--argjson lockoutTimeout "${LOCKOUT_TIMEOUT:-0}" \
|
--argjson lockoutTimeout "${LOCKOUT_TIMEOUT:-0}" \
|
||||||
'{unit:$unit,autoswitch:$autoswitch,enableTouch:$enableTouch,dualSwipe:$dualSwipe,swipeMode:$swipeMode,allowNavigation:$allowNavigation,homeTabIndex:$homeTabIndex,inactivityTimeout:$inactivityTimeout,enablePauseButton:$enablePauseButton,enableKeyboardButton:$enableKeyboardButton,enablePasswordProtection:$enablePasswordProtection,lockoutPassword:$lockoutPassword,lockoutTimeout:$lockoutTimeout,tabs:[]}' > "$tmp"
|
--arg lockoutAtTime "${LOCKOUT_AT_TIME:-}" \
|
||||||
|
--arg lockoutActiveStart "${LOCKOUT_ACTIVE_START:-}" \
|
||||||
|
--arg lockoutActiveEnd "${LOCKOUT_ACTIVE_END:-}" \
|
||||||
|
--argjson requirePasswordOnBoot "$boot_password_json" \
|
||||||
|
'{unit:$unit,autoswitch:$autoswitch,enableTouch:$enableTouch,dualSwipe:$dualSwipe,swipeMode:$swipeMode,allowNavigation:$allowNavigation,homeTabIndex:$homeTabIndex,inactivityTimeout:$inactivityTimeout,enablePauseButton:$enablePauseButton,enableKeyboardButton:$enableKeyboardButton,enablePasswordProtection:$enablePasswordProtection,lockoutPassword:$lockoutPassword,lockoutTimeout:$lockoutTimeout,lockoutAtTime:$lockoutAtTime,lockoutActiveStart:$lockoutActiveStart,lockoutActiveEnd:$lockoutActiveEnd,requirePasswordOnBoot:$requirePasswordOnBoot,tabs:[]}' > "$tmp"
|
||||||
|
|
||||||
if [[ ${#URLS[@]} -gt 0 ]]; then
|
if [[ ${#URLS[@]} -gt 0 ]]; then
|
||||||
for idx in "${!URLS[@]}"; do
|
for idx in "${!URLS[@]}"; do
|
||||||
@@ -3519,6 +3587,10 @@ let enableKeyboardButton=true;
|
|||||||
let enablePasswordProtection=false;
|
let enablePasswordProtection=false;
|
||||||
let lockoutPassword="";
|
let lockoutPassword="";
|
||||||
let lockoutTimeout=0;
|
let lockoutTimeout=0;
|
||||||
|
let lockoutAtTime="";
|
||||||
|
let lockoutActiveStart="";
|
||||||
|
let lockoutActiveEnd="";
|
||||||
|
let requirePasswordOnBoot=false;
|
||||||
|
|
||||||
// Password lockout state
|
// Password lockout state
|
||||||
let isLockedOut=false;
|
let isLockedOut=false;
|
||||||
@@ -3526,6 +3598,7 @@ let lockoutWindow=null;
|
|||||||
let lockoutTimer=null;
|
let lockoutTimer=null;
|
||||||
let lockoutActivityTime=Date.now();
|
let lockoutActivityTime=Date.now();
|
||||||
let requirePasswordAfterDisplay=false;
|
let requirePasswordAfterDisplay=false;
|
||||||
|
let lastScheduledLockCheck=0;
|
||||||
|
|
||||||
function loadConfig(){
|
function loadConfig(){
|
||||||
try{
|
try{
|
||||||
@@ -3545,12 +3618,19 @@ function loadConfig(){
|
|||||||
enablePasswordProtection=(config.enablePasswordProtection===true);
|
enablePasswordProtection=(config.enablePasswordProtection===true);
|
||||||
lockoutPassword=config.lockoutPassword||"";
|
lockoutPassword=config.lockoutPassword||"";
|
||||||
lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms
|
lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms
|
||||||
|
lockoutAtTime=config.lockoutAtTime||"";
|
||||||
|
lockoutActiveStart=config.lockoutActiveStart||"";
|
||||||
|
lockoutActiveEnd=config.lockoutActiveEnd||"";
|
||||||
|
requirePasswordOnBoot=(config.requirePasswordOnBoot===true);
|
||||||
|
|
||||||
console.log('[CONFIG] ═══════════════════════════');
|
console.log('[CONFIG] ═══════════════════════════');
|
||||||
console.log('[CONFIG] Home tab index:',homeTabIndex);
|
console.log('[CONFIG] Home tab index:',homeTabIndex);
|
||||||
console.log('[CONFIG] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
console.log('[CONFIG] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
||||||
console.log('[CONFIG] Navigation:',allowNavigation);
|
console.log('[CONFIG] Navigation:',allowNavigation);
|
||||||
console.log('[CONFIG] Pause button:',enablePauseButton); console.log('[CONFIG] Keyboard button:',enableKeyboardButton); console.log('[CONFIG] Password protection:',enablePasswordProtection); console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
console.log('[CONFIG] Pause button:',enablePauseButton); console.log('[CONFIG] Keyboard button:',enableKeyboardButton); console.log('[CONFIG] Password protection:',enablePasswordProtection); console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
||||||
|
if(lockoutAtTime)console.log('[CONFIG] Lock at time:',lockoutAtTime);
|
||||||
|
if(lockoutActiveStart&&lockoutActiveEnd)console.log('[CONFIG] Active hours:',lockoutActiveStart,'-',lockoutActiveEnd);
|
||||||
|
console.log('[CONFIG] Require password on boot:',requirePasswordOnBoot);
|
||||||
console.log('[CONFIG] Sites:',config.tabs?.length||0);
|
console.log('[CONFIG] Sites:',config.tabs?.length||0);
|
||||||
console.log('[CONFIG] ═══════════════════════════');
|
console.log('[CONFIG] ═══════════════════════════');
|
||||||
|
|
||||||
@@ -3728,15 +3808,64 @@ function unlockScreen(){
|
|||||||
lockoutActivityTime=Date.now();
|
lockoutActivityTime=Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: Check if current time is within active hours
|
||||||
|
function isWithinActiveHours(){
|
||||||
|
if(!lockoutActiveStart||!lockoutActiveEnd)return true; // No restriction
|
||||||
|
|
||||||
|
const now=new Date();
|
||||||
|
const currentTime=now.getHours()*60+now.getMinutes(); // Current time in minutes
|
||||||
|
|
||||||
|
const[startH,startM]=lockoutActiveStart.split(':').map(Number);
|
||||||
|
const[endH,endM]=lockoutActiveEnd.split(':').map(Number);
|
||||||
|
const startMinutes=startH*60+startM;
|
||||||
|
const endMinutes=endH*60+endM;
|
||||||
|
|
||||||
|
// Handle overnight ranges (e.g., 22:00-06:00)
|
||||||
|
if(startMinutes>endMinutes){
|
||||||
|
return currentTime>=startMinutes||currentTime<endMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentTime>=startMinutes&¤tTime<endMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Check if it's time to auto-lock
|
||||||
|
function checkScheduledLockTime(){
|
||||||
|
if(!lockoutAtTime||isLockedOut)return;
|
||||||
|
|
||||||
|
const now=new Date();
|
||||||
|
const[targetH,targetM]=lockoutAtTime.split(':').map(Number);
|
||||||
|
|
||||||
|
// Check once per minute to avoid multiple locks
|
||||||
|
const currentMinute=now.getHours()*60+now.getMinutes();
|
||||||
|
const targetMinute=targetH*60+targetM;
|
||||||
|
|
||||||
|
if(currentMinute===targetMinute&&lastScheduledLockCheck!==currentMinute){
|
||||||
|
lastScheduledLockCheck=currentMinute;
|
||||||
|
console.log('[LOCKOUT] Scheduled lock time reached: '+lockoutAtTime);
|
||||||
|
showLockoutScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function checkLockoutTimer(){
|
function checkLockoutTimer(){
|
||||||
if(!enablePasswordProtection||lockoutTimeout<=0||isLockedOut)return;
|
if(!enablePasswordProtection||isLockedOut)return;
|
||||||
|
|
||||||
const now=Date.now();
|
const now=Date.now();
|
||||||
const timeSinceActivity=now-lockoutActivityTime;
|
|
||||||
|
|
||||||
if(timeSinceActivity>=lockoutTimeout){
|
// Check for scheduled lock time
|
||||||
console.log('[LOCKOUT] Timeout reached, locking screen');
|
checkScheduledLockTime();
|
||||||
showLockoutScreen();
|
|
||||||
|
// Check inactivity-based lockout
|
||||||
|
if(lockoutTimeout>0){
|
||||||
|
// Check if within active hours (if configured)
|
||||||
|
if(!isWithinActiveHours()){
|
||||||
|
return; // Outside active hours, don't enforce lockout
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeSinceActivity=now-lockoutActivityTime;
|
||||||
|
if(timeSinceActivity>=lockoutTimeout){
|
||||||
|
console.log('[LOCKOUT] Timeout reached, locking screen');
|
||||||
|
showLockoutScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4600,6 +4729,14 @@ function createWindow(){
|
|||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
attachView(startIndex);
|
attachView(startIndex);
|
||||||
startMasterTimer();
|
startMasterTimer();
|
||||||
|
|
||||||
|
// Check for boot flag - require password on boot if configured
|
||||||
|
const bootFlag=path.join(__dirname,'.boot-flag');
|
||||||
|
if(enablePasswordProtection&&lockoutPassword&&requirePasswordOnBoot&&fs.existsSync(bootFlag)){
|
||||||
|
console.log('[LOCKOUT] Boot detected, requiring password');
|
||||||
|
fs.unlinkSync(bootFlag);
|
||||||
|
showLockoutScreen();
|
||||||
|
}
|
||||||
},1000);
|
},1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5924,6 +6061,9 @@ pactl set-source-mute @DEFAULT_SOURCE@ 0
|
|||||||
unclutter -idle 0.1 -root &
|
unclutter -idle 0.1 -root &
|
||||||
XDG_RUNTIME_DIR=/run/user/$(id -u) xbindkeys &
|
XDG_RUNTIME_DIR=/run/user/$(id -u) xbindkeys &
|
||||||
|
|
||||||
|
# Create boot flag for password requirement on boot
|
||||||
|
touch /home/kiosk/kiosk-app/.boot-flag
|
||||||
|
|
||||||
# Start kiosk app AFTER audio is ready
|
# Start kiosk app AFTER audio is ready
|
||||||
sleep 2
|
sleep 2
|
||||||
/home/kiosk/kiosk-app/start.sh &
|
/home/kiosk/kiosk-app/start.sh &
|
||||||
|
|||||||
Reference in New Issue
Block a user