mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
FEATURE: add plain text password toggle to settings (#22)
* FEATURE: add plain text password toggle to settings * clean up state types, lint, and add migrate Co-authored-by: austinried <4966622+austinried@users.noreply.github.com>
This commit is contained in:
parent
37214fcbdc
commit
9a6f8b86fc
@ -1,9 +1,19 @@
|
||||
import { GetAlbumList2Type } from '@app/subsonic/params'
|
||||
|
||||
export interface Server {
|
||||
export type Server = (TokenPassword | PlainPassword) & {
|
||||
id: string
|
||||
address: string
|
||||
username: string
|
||||
usePlainPassword: boolean
|
||||
}
|
||||
|
||||
interface PlainPassword {
|
||||
usePlainPassword: true
|
||||
plainPassword: string
|
||||
}
|
||||
|
||||
interface TokenPassword {
|
||||
usePlainPassword: false
|
||||
token: string
|
||||
salt: string
|
||||
}
|
||||
|
||||
@ -11,6 +11,9 @@ import md5 from 'md5'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { StyleSheet, Text, TextInput, View, ViewStyle } from 'react-native'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import SettingsSwitch from '@app/components/SettingsSwitch'
|
||||
|
||||
const PASSWORD_PLACEHOLDER = 'PASSWORD_PLACEHOLDER'
|
||||
|
||||
const ServerView: React.FC<{
|
||||
id?: string
|
||||
@ -26,7 +29,12 @@ const ServerView: React.FC<{
|
||||
|
||||
const [address, setAddress] = useState(server?.address || '')
|
||||
const [username, setUsername] = useState(server?.username || '')
|
||||
const [password, setPassword] = useState(server?.token ? 'password' : '')
|
||||
|
||||
const [usePlainPassword, setUsePlainPassword] = useState(server?.usePlainPassword ?? false)
|
||||
const [password, setPassword] = useState(
|
||||
server?.usePlainPassword ? server.plainPassword || '' : server?.token ? PASSWORD_PLACEHOLDER : '',
|
||||
)
|
||||
|
||||
const [testing, setTesting] = useState(false)
|
||||
const [removing, setRemoving] = useState(false)
|
||||
const [saving, setSaving] = useState(false)
|
||||
@ -48,11 +56,24 @@ const ServerView: React.FC<{
|
||||
}, [navigation])
|
||||
|
||||
const createServer = useCallback<() => Server>(() => {
|
||||
const salt = server?.salt || uuidv4()
|
||||
if (usePlainPassword) {
|
||||
return {
|
||||
id: server?.id || uuidv4(),
|
||||
usePlainPassword,
|
||||
plainPassword: password,
|
||||
address,
|
||||
username,
|
||||
}
|
||||
}
|
||||
|
||||
let token: string
|
||||
if (password === 'password' && server?.token) {
|
||||
let salt: string
|
||||
|
||||
if (server && !server.usePlainPassword && password === PASSWORD_PLACEHOLDER) {
|
||||
salt = server.salt
|
||||
token = server.token
|
||||
} else {
|
||||
salt = uuidv4()
|
||||
token = md5(password + salt)
|
||||
}
|
||||
|
||||
@ -60,10 +81,11 @@ const ServerView: React.FC<{
|
||||
id: server?.id || uuidv4(),
|
||||
address,
|
||||
username,
|
||||
usePlainPassword,
|
||||
salt,
|
||||
token,
|
||||
}
|
||||
}, [address, password, server?.id, server?.salt, server?.token, username])
|
||||
}, [usePlainPassword, server, address, username, password])
|
||||
|
||||
const save = useCallback(() => {
|
||||
if (!validate()) {
|
||||
@ -105,6 +127,25 @@ const ServerView: React.FC<{
|
||||
waitForRemove()
|
||||
}, [canRemove, exit, id, removeServer])
|
||||
|
||||
const togglePlainPassword = useCallback(
|
||||
(value: boolean) => {
|
||||
setUsePlainPassword(value)
|
||||
|
||||
if (value) {
|
||||
if (server && server.usePlainPassword) {
|
||||
setPassword(server.plainPassword)
|
||||
} else if (server) {
|
||||
setPassword('')
|
||||
}
|
||||
} else {
|
||||
if (server && !server.usePlainPassword) {
|
||||
setPassword(PASSWORD_PLACEHOLDER)
|
||||
}
|
||||
}
|
||||
},
|
||||
[server],
|
||||
)
|
||||
|
||||
const test = useCallback(() => {
|
||||
setTesting(true)
|
||||
const potential = createServer()
|
||||
@ -180,6 +221,16 @@ const ServerView: React.FC<{
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
title="Force plain text password"
|
||||
subtitle={
|
||||
usePlainPassword
|
||||
? 'Send password in plain text (legacy, make sure your connection is secure!)'
|
||||
: 'Send password as token + salt'
|
||||
}
|
||||
value={usePlainPassword}
|
||||
setValue={togglePlainPassword}
|
||||
/>
|
||||
<Button
|
||||
disabled={disableControls()}
|
||||
style={styles.button}
|
||||
|
||||
11
app/state/migrations.ts
Normal file
11
app/state/migrations.ts
Normal file
@ -0,0 +1,11 @@
|
||||
const migrations: Array<(state: any) => any> = [
|
||||
state => {
|
||||
for (let server of state.settings.servers) {
|
||||
server.usePlainPassword = false
|
||||
}
|
||||
|
||||
return state
|
||||
},
|
||||
]
|
||||
|
||||
export default migrations
|
||||
@ -4,10 +4,13 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import create from 'zustand'
|
||||
import { persist, StateStorage } from 'zustand/middleware'
|
||||
import { CacheSlice, createCacheSlice } from './cache'
|
||||
import migrations from './migrations'
|
||||
import { createMusicMapSlice, MusicMapSlice } from './musicmap'
|
||||
import { createTrackPlayerSlice, TrackPlayerSlice } from './trackplayer'
|
||||
import { createTrackPlayerMapSlice, TrackPlayerMapSlice } from './trackplayermap'
|
||||
|
||||
const DB_VERSION = migrations.length
|
||||
|
||||
export type Store = SettingsSlice &
|
||||
MusicSlice &
|
||||
MusicMapSlice &
|
||||
@ -51,6 +54,7 @@ export const useStore = create<Store>(
|
||||
}),
|
||||
{
|
||||
name: '@appStore',
|
||||
version: DB_VERSION,
|
||||
getStorage: () => storage,
|
||||
whitelist: ['settings', 'cacheFiles'],
|
||||
onRehydrateStorage: _preState => {
|
||||
@ -59,6 +63,17 @@ export const useStore = create<Store>(
|
||||
postState?.setHydrated(true)
|
||||
}
|
||||
},
|
||||
migrate: (persistedState, version) => {
|
||||
if (version > DB_VERSION) {
|
||||
throw new Error('cannot migrate db on a downgrade, delete all data first')
|
||||
}
|
||||
|
||||
for (let i = version; i < DB_VERSION; i++) {
|
||||
persistedState = migrations[i](persistedState)
|
||||
}
|
||||
|
||||
return persistedState
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@ -64,8 +64,14 @@ export class SubsonicApiClient {
|
||||
|
||||
this.params = new URLSearchParams()
|
||||
this.params.append('u', server.username)
|
||||
this.params.append('t', server.token)
|
||||
this.params.append('s', server.salt)
|
||||
|
||||
if (server.usePlainPassword) {
|
||||
this.params.append('p', server.plainPassword)
|
||||
} else {
|
||||
this.params.append('t', server.token)
|
||||
this.params.append('s', server.salt)
|
||||
}
|
||||
|
||||
this.params.append('v', '1.15.0')
|
||||
this.params.append('c', 'subtracks')
|
||||
}
|
||||
|
||||
@ -74,13 +74,6 @@
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user