Fix saving profile and add generating new API key

This commit is contained in:
Paul Bienkowski 2021-11-30 23:32:42 +01:00
parent 2f375dc24d
commit 004ad46251
2 changed files with 55 additions and 16 deletions

View file

@ -1,6 +1,9 @@
import logging import logging
import os
import binascii
from sanic.response import json from sanic.response import json
from sanic.exceptions import InvalidUsage
from obs.api.app import api, require_auth from obs.api.app import api, require_auth
@ -16,7 +19,7 @@ def user_to_json(user):
"bio": user.bio, "bio": user.bio,
"image": user.image, "image": user.image,
"areTracksVisibleForAll": user.are_tracks_visible_for_all, "areTracksVisibleForAll": user.are_tracks_visible_for_all,
# "apiKey": user.api_key, "apiKey": user.api_key,
} }
@ -29,13 +32,17 @@ async def get_user(req):
@require_auth @require_auth
async def put_user(req): async def put_user(req):
user = req.ctx.user user = req.ctx.user
data = req.json
for key in ["username", "email", "bio", "image"]: for key in ["email", "bio", "image"]:
if key in req.json and isinstance(req.json[key], (str, type(None))): if key in data and isinstance(data[key], (str, type(None))):
setattr(user, key, req.json[key]) setattr(user, key, data[key])
if "areTracksVisibleForAll" in req.json: if "areTracksVisibleForAll" in data:
user.are_tracks_visible_for_all = bool(req.json["areTracksVisibleForAll"]) user.are_tracks_visible_for_all = bool(data["areTracksVisibleForAll"])
if data.get("updateApiKey"):
user.api_key = binascii.b2a_hex(os.urandom(16)).decode("ascii")
await req.ctx.db.commit() await req.ctx.db.commit()
return json(user_to_json(req.ctx.user)) return json(user_to_json(req.ctx.user))

View file

@ -19,8 +19,8 @@ const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(func
setLoading(true) setLoading(true)
setErrors(null) setErrors(null)
try { try {
const response = await api.put('/user', {body: {user: changes}}) const response = await api.put('/user', {body: changes})
setLogin(response.user) setLogin(response)
} catch (err) { } catch (err) {
setErrors(err.errors) setErrors(err.errors)
} finally { } finally {
@ -30,6 +30,19 @@ const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(func
[setLoading, setLogin, setErrors] [setLoading, setLogin, setErrors]
) )
const onGenerateNewKey = React.useCallback(async () => {
setLoading(true)
setErrors(null)
try {
const response = await api.put('/user', {body: {updateApiKey: true}})
setLogin(response)
} catch (err) {
setErrors(err.errors)
} finally {
setLoading(false)
}
}, [setLoading, setLogin, setErrors])
return ( return (
<Page> <Page>
<Grid centered relaxed divided> <Grid centered relaxed divided>
@ -41,7 +54,7 @@ const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(func
<Form onSubmit={handleSubmit(onSave)} loading={loading}> <Form onSubmit={handleSubmit(onSave)} loading={loading}>
<Ref innerRef={findInput(register)}> <Ref innerRef={findInput(register)}>
<Form.Input error={errors?.username} label="Username" name="username" defaultValue={login.username} /> <Form.Input error={errors?.username} label="Username" name="username" defaultValue={login.username} disabled />
</Ref> </Ref>
<Form.Field error={errors?.bio}> <Form.Field error={errors?.bio}>
<label>Bio</label> <label>Bio</label>
@ -62,7 +75,7 @@ const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(func
</Form> </Form>
</Grid.Column> </Grid.Column>
<Grid.Column width={6}> <Grid.Column width={6}>
<ApiKeyDialog {...{login}} /> <ApiKeyDialog {...{login, onGenerateNewKey}} />
<Divider /> <Divider />
@ -101,7 +114,7 @@ function CopyInput({value, ...props}) {
const selectField = findInput((ref) => ref?.select()) const selectField = findInput((ref) => ref?.select())
function ApiKeyDialog({login}) { function ApiKeyDialog({login, onGenerateNewKey}) {
const config = useConfig() const config = useConfig()
const [show, setShow] = React.useState(false) const [show, setShow] = React.useState(false)
const onClick = React.useCallback( const onClick = React.useCallback(
@ -112,6 +125,14 @@ function ApiKeyDialog({login}) {
[setShow] [setShow]
) )
const onGenerateNewKeyInner = React.useCallback(
(e) => {
e.preventDefault()
onGenerateNewKey()
},
[onGenerateNewKey]
)
return ( return (
<> <>
<Header as="h2">Your API Key</Header> <Header as="h2">Your API Key</Header>
@ -120,11 +141,15 @@ function ApiKeyDialog({login}) {
configuration interface to allow direct upload from the device. configuration interface to allow direct upload from the device.
</p> </p>
<p>Please protect your API Key carefully as it allows full control over your account.</p> <p>Please protect your API Key carefully as it allows full control over your account.</p>
<div style={{height: 40, marginBottom: 16}}> <div style={{minHeight: 40, marginBottom: 16}}>
{show ? ( {show ? (
<Ref innerRef={selectField}> login.apiKey ? (
<CopyInput label="Personal API key" value={login.apiKey} /> <Ref innerRef={selectField}>
</Ref> <CopyInput label="Personal API Key" value={login.apiKey} />
</Ref>
) : (
<Message warning content='You have no API Key, please generate one below.' />
)
) : ( ) : (
<Button onClick={onClick}> <Button onClick={onClick}>
<Icon name="lock" /> Show API Key <Icon name="lock" /> Show API Key
@ -132,7 +157,14 @@ function ApiKeyDialog({login}) {
)} )}
</div> </div>
<p>The API URL should be set to:</p> <p>The API URL should be set to:</p>
<CopyInput label="API URL" value={config?.apiUrl ?? '...'} /> <div style={{marginBottom: 16}}>
<CopyInput label="API URL" value={config?.apiUrl ?? '...'} />
</div>
<p>
You can generate a new API Key here, which will invalidate the old one, disconnecting all devices you used it on
from your account.
</p>
<Button onClick={onGenerateNewKeyInner}>Generate new API key</Button>
</> </>
) )
} }