Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions frontend/app/components/BackendAdd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,8 @@ const getAccessToken = async (): Promise<boolean | undefined> => {
const getUsers = async (
showAlert: boolean = true,
forceReload: boolean = false,
withTokens: boolean = false,
targetUser: string | null = null,
): Promise<Array<BackendUser> | undefined> => {
const required_values = ['type', 'token', 'url', 'uuid'];

Expand Down Expand Up @@ -963,7 +965,12 @@ const getUsers = async (
}

const query = new URLSearchParams();
query.append('tokens', '1');
if (withTokens) {
query.append('tokens', '1');
if (targetUser) {
query.append('target_user', targetUser);
}
}
if (forceReload) {
query.append('no_cache', '1');
}
Expand Down Expand Up @@ -1091,6 +1098,30 @@ const changeStep = async (): Promise<void> => {
return;
}

if ('plex' === backend.value.type) {
const selected = users.value.find((u) => u.id === backend.value.user);
if (!selected) {
notification('error', 'Error', 'Selected user not found.');
return;
}

const usersResponse = await getUsers(true, true, true, selected.uuid ?? selected.id);
const updated = usersResponse?.find((u) => u.id === selected.id);
const token = updated?.token ?? selected.token;

if (!token) {
notification('error', 'Error', 'Selected user does not have a valid token.');
return;
}

if (!backend.value.options?.ADMIN_TOKEN) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
}
backend.value.token = token;
backend.value.options.plex_user_name = updated?.name ?? selected.name;
backend.value.options.plex_user_uuid = updated?.uuid ?? selected.uuid ?? '';
}

stage.value = 4;
}

Expand All @@ -1112,11 +1143,18 @@ const addBackend = async (): Promise<boolean> => {
}

if ('plex' === backend.value.type) {
const token = users.value.find((u) => u.id === backend.value.user)?.token;
const selectedUser = users.value.find((u) => u.id === backend.value.user);
const token = selectedUser?.token;
if (token && token !== backend.value.token) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
if (!backend.value.options?.ADMIN_TOKEN) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
}
backend.value.token = token;
}
if (selectedUser) {
backend.value.options.plex_user_name = selectedUser.name;
backend.value.options.plex_user_uuid = selectedUser.uuid ?? '';
}
}

if (isLimited.value) {
Expand Down
62 changes: 58 additions & 4 deletions frontend/app/pages/backend/[backend]/edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,17 @@
<span class="is-hidden-mobile">Reload</span>
</button>
</div>
<div class="control" v-if="'plex' === backend.type && !isLimitedToken">
<button
class="button is-info"
type="button"
:disabled="usersLoading || !backend.user"
@click="() => generateUserToken()"
>
<span class="icon"><i class="fas fa-key" /></span>
<span class="is-hidden-mobile">Generate Token</span>
</button>
</div>
</div>
</div>
<p class="help">
Expand Down Expand Up @@ -781,7 +792,12 @@ const getUUid = async (): Promise<void> => {
backend.value.uuid = json.identifier;
};

const getUsers = async (showAlert: boolean = true, forceReload: boolean = false): Promise<void> => {
const getUsers = async (
showAlert: boolean = true,
forceReload: boolean = false,
withTokens: boolean = false,
targetUser: string | null = null,
): Promise<Array<BackendEditUser> | undefined> => {
const required_values: Array<keyof Backend> = ['type', 'token', 'url', 'uuid'];

if (required_values.some((v) => !backend.value[v])) {
Expand Down Expand Up @@ -814,7 +830,12 @@ const getUsers = async (showAlert: boolean = true, forceReload: boolean = false)
});

const query = new URLSearchParams();
query.append('tokens', '1');
if (withTokens) {
query.append('tokens', '1');
if (targetUser) {
query.append('target_user', targetUser);
}
}
if (forceReload) {
query.append('no_cache', '1');
}
Expand Down Expand Up @@ -842,6 +863,37 @@ const getUsers = async (showAlert: boolean = true, forceReload: boolean = false)
}

users.value = json;
return users.value;
};

const generateUserToken = async (): Promise<void> => {
if ('plex' !== backend.value.type) {
return;
}

if (!backend.value.user) {
notification('error', 'Error', 'Select a user to generate a token.');
return;
}

const selectedUser = users.value.find((user) => user.id === backend.value.user);
if (!selectedUser) {
notification('error', 'Error', 'Selected user not found.');
return;
}

const usersResponse = await getUsers(true, true, true, selectedUser.uuid ?? selectedUser.id);
const updated = usersResponse?.find((user) => user.id === selectedUser.id);
const token = updated?.token ?? selectedUser.token;
if (!token) {
notification('error', 'Error', 'User token not found');
return;
}

if (!backend.value.options?.ADMIN_TOKEN) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
}
backend.value.token = token;
};

// -- if users updated we need to reset the token in-case the plex auth changed
Expand Down Expand Up @@ -870,12 +922,14 @@ watch(

// Check if the user has a token
if (!selectedUser.token) {
notification('error', 'Error', 'Selected user does not have a valid token');
return;
}

// Only update if the token has actually changed
if (selectedUser.token !== backend.value.token) {
if (!backend.value.options?.ADMIN_TOKEN) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
}
backend.value.token = selectedUser.token;
notification('info', 'Information', `Token updated for user: ${selectedUser.name}`);
}
Expand Down Expand Up @@ -1014,11 +1068,11 @@ watch(
backend.value.options.plex_user_uuid = selectedUser.uuid;

if (!selectedUser.token) {
notification('error', 'Error', 'User token not found');
return;
}

if (selectedUser.token !== backend.value.token) {
backend.value.options.ADMIN_TOKEN = backend.value.token;
backend.value.token = selectedUser.token;
notification('info', 'Information', `Token updated for user: ${selectedUser.name}`);
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ export interface BackendUser {
token?: string;
/** Optional user type */
type?: string;
/** Optional user uuid */
uuid?: string;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/API/Backend/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public function __invoke(iRequest $request, string $name, iImport $mapper, iLogg
$opts['tokens'] = true;
}

if (null !== ($user = $params->get(Options::TARGET_USER, null))) {
$opts[Options::TARGET_USER] = $user;
}

if (true === (bool) $params->get('raw', false)) {
$opts[Options::RAW_RESPONSE] = true;
}
Expand Down
4 changes: 4 additions & 0 deletions src/API/Backends/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public function __invoke(iRequest $request, string $type, iLogger $logger): iRes
$opts[Options::GET_TOKENS] = true;
}

if (null !== ($user = $params->get(Options::TARGET_USER, null))) {
$opts[Options::TARGET_USER] = $user;
}

if (true === (bool) $params->get('no_cache', false)) {
$opts[Options::NO_CACHE] = true;
}
Expand Down
6 changes: 5 additions & 1 deletion src/Backends/Plex/Action/GetUserToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private function getUserToken(Context $context, int|string $userId, string $user
'url' => (string) $url,
]);

$opts['user_info'] = ['username' => $username];
$opts['user_info'] = ['username' => $username, 'user_id' => $userId];

$response = $this->request(Method::POST, $url, Status::CREATED, $context, array_replace_recursive([
'headers' => ['Accept' => 'application/json'],
Expand Down Expand Up @@ -141,6 +141,10 @@ private function getUserToken(Context $context, int|string $userId, string $user
],
], $opts));

if (true === $response instanceof Response) {
return $response;
}

$json = json_decode(
json: $response->getContent(),
associative: true,
Expand Down
51 changes: 33 additions & 18 deletions src/Backends/Plex/Action/GetUsersList.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private function getHomeUsers(Response $users, Context $context, array $opts = [
'Accept' => 'application/json',
],
]);
} catch (InvalidArgumentException $e) {
} catch (InvalidArgumentException|iException $e) {
return new Response(
status: false,
error: new Error(
Expand Down Expand Up @@ -219,6 +219,12 @@ private function processHomeUsers(

$external = $users->isSuccessful() ? $users->response : [];

if (null !== ($targetUser = ag($opts, Options::TARGET_USER, null))) {
if ('' === ($targetUser = (string) $targetUser)) {
$targetUser = null;
}
}

$list = [];

foreach (ag($json, 'users', []) as $data) {
Expand All @@ -243,23 +249,32 @@ private function processHomeUsers(
];

if (true === (bool) ag($opts, Options::GET_TOKENS)) {
$tokenRequest = Container::getNew(GetUserToken::class)(
context: $context,
userId: ag($data, 'uuid'),
username: ag($data, 'name'),
);

if ($tokenRequest->hasError() && $tokenRequest->error) {
$this->logger->log(
$tokenRequest->error->level(),
$tokenRequest->error->message,
$tokenRequest->error->context,
$matchesTarget =
null === $targetUser
|| (string) $targetUser === (string) ag($data, 'id')
|| (string) $targetUser === (string) ag($data, 'uuid');

if (true === $matchesTarget) {
$tokenRequest = Container::getNew(GetUserToken::class)(
context: $context,
userId: ag($data, 'uuid'),
username: ag($data, 'name'),
);
}

$data['token'] = $tokenRequest->isSuccessful() ? $tokenRequest->response : null;
if (true === $tokenRequest->hasError() && $tokenRequest->error) {
$data['token_error'] = ag($tokenRequest->error->extra, 'error', $tokenRequest->error->format());
if ($tokenRequest->hasError() && $tokenRequest->error) {
$this->logger->log(
$tokenRequest->error->level(),
$tokenRequest->error->message,
$tokenRequest->error->context,
);
}

$data['token'] = $tokenRequest->isSuccessful() ? $tokenRequest->response : null;
if (true === $tokenRequest->hasError() && $tokenRequest->error) {
$data['token_error'] = ag($tokenRequest->error->extra, 'error', $tokenRequest->error->format());
}
} elseif (null !== $targetUser) {
$data['token'] = null;
}
}

Expand Down Expand Up @@ -340,7 +355,7 @@ private function getExternalUsers(Context $context, array $opts = []): Response
if (true !== (bool) ag($opts, Options::GET_TOKENS) || count($users) < 1) {
return new Response(status: true, response: $users);
}
} catch (InvalidArgumentException $e) {
} catch (InvalidArgumentException|iException $e) {
return new Response(
status: false,
error: new Error(
Expand Down Expand Up @@ -377,7 +392,7 @@ private function getExternalUsers(Context $context, array $opts = []): Response
'Accept' => 'application/xml',
],
]);
} catch (InvalidArgumentException $e) {
} catch (InvalidArgumentException|iException $e) {
return new Response(
status: false,
error: new Error(
Expand Down
1 change: 1 addition & 0 deletions src/Libs/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ final class Options
public const string ALT_NAME = 'ALT_NAME';
public const string ALT_ID = 'ALT_ID';
public const string CONTEXT_USER = 'CONTEXT_USER';
public const string TARGET_USER = 'target_user';
public const string GET_TOKENS = 'tokens';
public const string LOG_CONTEXT = 'LOG_CONTEXT';
public const string DELAY_BY = 'DELAY_BY';
Expand Down
Loading