fix: remove special character requirement from stream keys (#4754)
Some broadcasting software (e.g., Prism Live Studio) strips special characters from stream keys, making Owncast-generated keys incompatible. Changes: - Create separate STREAM_KEY_COMPLEXITY_RULES without special char requirement - Update generateRndKey() to only use alphanumeric characters - Keep PASSWORD_COMPLEXITY_RULES unchanged for admin password security - Update tooltip text to reflect new requirements - Add comprehensive tests for stream key generation Fixes #4690 Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
93b482871f
commit
397ec95d9b
@@ -1,10 +1,10 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { Table, Space, Button, Typography, Alert, Input, Form, message } from 'antd';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ServerStatusContext } from '../../../../utils/server-status-context';
|
||||
|
||||
import { fetchData, UPDATE_STREAM_KEYS } from '../../../../utils/apis';
|
||||
import { PASSWORD_COMPLEXITY_RULES, REGEX_PASSWORD } from '../../../../utils/config-constants';
|
||||
import { STREAM_KEY_COMPLEXITY_RULES, REGEX_STREAM_KEY } from '../../../../utils/config-constants';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
@@ -37,14 +37,13 @@ const saveKeys = async (keys, setError) => {
|
||||
export const generateRndKey = () => {
|
||||
let defaultKey = '';
|
||||
let isValidStreamKey = false;
|
||||
const streamKeyRegex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$^&*]).{8,192}$/;
|
||||
const s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$^&*';
|
||||
const s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
while (!isValidStreamKey) {
|
||||
const temp = Array.apply(20, Array(30))
|
||||
.map(() => s.charAt(Math.floor(Math.random() * s.length)))
|
||||
.join('');
|
||||
if (streamKeyRegex.test(temp)) {
|
||||
if (REGEX_STREAM_KEY.test(temp)) {
|
||||
isValidStreamKey = true;
|
||||
defaultKey = temp;
|
||||
}
|
||||
@@ -57,15 +56,6 @@ const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setE
|
||||
const [form] = Form.useForm();
|
||||
const { Item } = Form;
|
||||
|
||||
// Password Complexity rules
|
||||
const passwordComplexityRules = [];
|
||||
|
||||
useEffect(() => {
|
||||
PASSWORD_COMPLEXITY_RULES.forEach(element => {
|
||||
passwordComplexityRules.push(element);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleAddKey = (newkey: any) => {
|
||||
const updatedKeys = [...streamKeys, newkey];
|
||||
|
||||
@@ -81,7 +71,7 @@ const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setE
|
||||
|
||||
const handleInputChange = (event: any) => {
|
||||
const val = event.target.value;
|
||||
if (REGEX_PASSWORD.test(val)) {
|
||||
if (REGEX_STREAM_KEY.test(val)) {
|
||||
setHasChanged(true);
|
||||
} else {
|
||||
setHasChanged(false);
|
||||
@@ -108,10 +98,10 @@ const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setE
|
||||
<p>
|
||||
The key you provide your broadcasting software. Please note that the key must be a
|
||||
minimum of eight characters and must include at least one uppercase letter, at least one
|
||||
lowercase letter, at least one special character, and at least one number.
|
||||
lowercase letter, and at least one number.
|
||||
</p>
|
||||
}
|
||||
rules={PASSWORD_COMPLEXITY_RULES}
|
||||
rules={STREAM_KEY_COMPLEXITY_RULES}
|
||||
>
|
||||
<Input placeholder="your key" onChange={handleInputChange} />
|
||||
</Item>
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { generateRndKey } from '../components/admin/config/server/StreamKeys';
|
||||
import { REGEX_STREAM_KEY } from '../utils/config-constants';
|
||||
|
||||
describe('generateRndKey', () => {
|
||||
test('should generate a key that matches the regular expression', () => {
|
||||
test('should generate a key that matches the stream key regex', () => {
|
||||
const key = generateRndKey();
|
||||
const regex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$^&*]).{8,192}$/;
|
||||
expect(regex.test(key)).toBe(true);
|
||||
// Use the same regex constant that the implementation uses
|
||||
expect(REGEX_STREAM_KEY.test(key)).toBe(true);
|
||||
});
|
||||
|
||||
test('should only contain alphanumeric characters (no special characters for broadcasting software compatibility)', () => {
|
||||
const key = generateRndKey();
|
||||
// Keys should be purely alphanumeric for compatibility with broadcasting software
|
||||
expect(key).toMatch(/^[a-zA-Z0-9]+$/);
|
||||
});
|
||||
|
||||
test('should not contain dashes', () => {
|
||||
const key = generateRndKey();
|
||||
// Dashes are explicitly forbidden as they can break RTMP URL parsing
|
||||
expect(key).not.toContain('-');
|
||||
});
|
||||
|
||||
test('returns a string', () => {
|
||||
@@ -23,4 +36,20 @@ describe('generateRndKey', () => {
|
||||
const key2 = generateRndKey();
|
||||
expect(key1).not.toBe(key2);
|
||||
});
|
||||
|
||||
test('should contain at least one uppercase letter, one lowercase letter, and one digit', () => {
|
||||
const key = generateRndKey();
|
||||
expect(key).toMatch(/[A-Z]/); // has uppercase
|
||||
expect(key).toMatch(/[a-z]/); // has lowercase
|
||||
expect(key).toMatch(/[0-9]/); // has digit
|
||||
});
|
||||
|
||||
test('should consistently generate valid keys across multiple invocations', () => {
|
||||
// Generate multiple keys to ensure consistency
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const key = generateRndKey();
|
||||
expect(REGEX_STREAM_KEY.test(key)).toBe(true);
|
||||
expect(key).toMatch(/^[a-zA-Z0-9]+$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -639,3 +639,28 @@ export const PASSWORD_COMPLEXITY_RULES = [
|
||||
];
|
||||
|
||||
export const REGEX_PASSWORD = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$%^&*])[^-]{8,192}$/;
|
||||
|
||||
// Stream key validation rules - same as password but WITHOUT special character requirement
|
||||
// This is needed because some broadcasting software (e.g., Prism Live Studio) strips special characters
|
||||
export const STREAM_KEY_COMPLEXITY_RULES = [
|
||||
{ min: 8, message: '- minimum 8 characters' },
|
||||
{ max: 192, message: '- maximum 192 characters' },
|
||||
{
|
||||
pattern: /^(?=.*[a-z])/,
|
||||
message: '- at least one lowercase letter',
|
||||
},
|
||||
{
|
||||
pattern: /^(?=.*[A-Z])/,
|
||||
message: '- at least one uppercase letter',
|
||||
},
|
||||
{
|
||||
pattern: /\d/,
|
||||
message: '- at least one digit',
|
||||
},
|
||||
{
|
||||
pattern: /^[^-]+$/,
|
||||
message: '- must NOT contain a dash: -',
|
||||
},
|
||||
];
|
||||
|
||||
export const REGEX_STREAM_KEY = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])[^-]{8,192}$/;
|
||||
|
||||
Reference in New Issue
Block a user