mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
fix(plex): do not fail to import Plex users when Plex Home has managed users (#1699)
* fix(plex): do not fail to import Plex users when Plex Home has managed users * fix: default display name to email when user has no username also, do not set username or plexUsername when it is the same as the user's email address * fix(ui): user display name placeholder should reflect fallback logic if username is not set * fix(ui): hide email addresses of other users if logged-in user does not have Manage Users permission * fix: always set Plex username even if same as user's email * fix: remove unnecessary permission check * fix: transform email addresses to lowercase
This commit is contained in:
@@ -48,11 +48,17 @@ export class User {
|
|||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id: number;
|
public id: number;
|
||||||
|
|
||||||
@Column({ unique: true })
|
@Column({
|
||||||
|
unique: true,
|
||||||
|
transformer: {
|
||||||
|
from: (value: string): string => value.toLowerCase(),
|
||||||
|
to: (value: string): string => value.toLowerCase(),
|
||||||
|
},
|
||||||
|
})
|
||||||
public email: string;
|
public email: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
public plexUsername: string;
|
public plexUsername?: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
public username?: string;
|
public username?: string;
|
||||||
@@ -220,7 +226,7 @@ export class User {
|
|||||||
|
|
||||||
@AfterLoad()
|
@AfterLoad()
|
||||||
public setDisplayName(): void {
|
public setDisplayName(): void {
|
||||||
this.displayName = this.username || this.plexUsername;
|
this.displayName = this.username || this.plexUsername || this.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getQuota(): Promise<QuotaResponse> {
|
public async getQuota(): Promise<QuotaResponse> {
|
||||||
|
@@ -43,7 +43,7 @@ authRoutes.post('/plex', async (req, res, next) => {
|
|||||||
let user = await userRepository
|
let user = await userRepository
|
||||||
.createQueryBuilder('user')
|
.createQueryBuilder('user')
|
||||||
.where('user.plexId = :id', { id: account.id })
|
.where('user.plexId = :id', { id: account.id })
|
||||||
.orWhere('LOWER(user.email) = :email', {
|
.orWhere('user.email = :email', {
|
||||||
email: account.email.toLowerCase(),
|
email: account.email.toLowerCase(),
|
||||||
})
|
})
|
||||||
.getOne();
|
.getOne();
|
||||||
@@ -65,9 +65,6 @@ authRoutes.post('/plex', async (req, res, next) => {
|
|||||||
user.plexId = account.id;
|
user.plexId = account.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.username === account.username) {
|
|
||||||
user.username = '';
|
|
||||||
}
|
|
||||||
await userRepository.save(user);
|
await userRepository.save(user);
|
||||||
} else {
|
} else {
|
||||||
// Here we check if it's the first user. If it is, we create the user with no check
|
// Here we check if it's the first user. If it is, we create the user with no check
|
||||||
@@ -177,7 +174,7 @@ authRoutes.post('/local', async (req, res, next) => {
|
|||||||
const user = await userRepository
|
const user = await userRepository
|
||||||
.createQueryBuilder('user')
|
.createQueryBuilder('user')
|
||||||
.select(['user.id', 'user.password'])
|
.select(['user.id', 'user.password'])
|
||||||
.where('LOWER(user.email) = :email', { email: body.email.toLowerCase() })
|
.where('user.email = :email', { email: body.email.toLowerCase() })
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
const isCorrectCredentials = await user?.passwordMatch(body.password);
|
const isCorrectCredentials = await user?.passwordMatch(body.password);
|
||||||
@@ -244,7 +241,7 @@ authRoutes.post('/reset-password', async (req, res) => {
|
|||||||
|
|
||||||
const user = await userRepository
|
const user = await userRepository
|
||||||
.createQueryBuilder('user')
|
.createQueryBuilder('user')
|
||||||
.where('LOWER(user.email) = :email', { email: body.email.toLowerCase() })
|
.where('user.email = :email', { email: body.email.toLowerCase() })
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
@@ -31,7 +31,7 @@ router.get('/', async (req, res, next) => {
|
|||||||
break;
|
break;
|
||||||
case 'displayname':
|
case 'displayname':
|
||||||
query = query.orderBy(
|
query = query.orderBy(
|
||||||
'(CASE WHEN user.username IS NULL THEN user.plexUsername ELSE user.username END)',
|
"(CASE WHEN (user.username IS NULL OR user.username = '') THEN (CASE WHEN (user.plexUsername IS NULL OR user.plexUsername = '') THEN user.email ELSE LOWER(user.plexUsername) END) ELSE LOWER(user.username) END)",
|
||||||
'ASC'
|
'ASC'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -84,7 +84,7 @@ router.post(
|
|||||||
|
|
||||||
const existingUser = await userRepository
|
const existingUser = await userRepository
|
||||||
.createQueryBuilder('user')
|
.createQueryBuilder('user')
|
||||||
.where('LOWER(user.email) = :email', {
|
.where('user.email = :email', {
|
||||||
email: body.email.toLowerCase(),
|
email: body.email.toLowerCase(),
|
||||||
})
|
})
|
||||||
.getOne();
|
.getOne();
|
||||||
@@ -396,10 +396,11 @@ router.post(
|
|||||||
for (const rawUser of plexUsersResponse.MediaContainer.User) {
|
for (const rawUser of plexUsersResponse.MediaContainer.User) {
|
||||||
const account = rawUser.$;
|
const account = rawUser.$;
|
||||||
|
|
||||||
|
if (account.email) {
|
||||||
const user = await userRepository
|
const user = await userRepository
|
||||||
.createQueryBuilder('user')
|
.createQueryBuilder('user')
|
||||||
.where('user.plexId = :id', { id: account.id })
|
.where('user.plexId = :id', { id: account.id })
|
||||||
.orWhere('LOWER(user.email) = :email', {
|
.orWhere('user.email = :email', {
|
||||||
email: account.email.toLowerCase(),
|
email: account.email.toLowerCase(),
|
||||||
})
|
})
|
||||||
.getOne();
|
.getOne();
|
||||||
@@ -414,19 +415,10 @@ router.post(
|
|||||||
if (user.userType === UserType.LOCAL) {
|
if (user.userType === UserType.LOCAL) {
|
||||||
user.userType = UserType.PLEX;
|
user.userType = UserType.PLEX;
|
||||||
user.plexId = parseInt(account.id);
|
user.plexId = parseInt(account.id);
|
||||||
|
|
||||||
if (user.username === account.username) {
|
|
||||||
user.username = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await userRepository.save(user);
|
await userRepository.save(user);
|
||||||
} else {
|
} else {
|
||||||
// Check to make sure it's a real account
|
if (await mainPlexTv.checkUserAccess(parseInt(account.id))) {
|
||||||
if (
|
|
||||||
account.email &&
|
|
||||||
account.username &&
|
|
||||||
(await mainPlexTv.checkUserAccess(parseInt(account.id)))
|
|
||||||
) {
|
|
||||||
const newUser = new User({
|
const newUser = new User({
|
||||||
plexUsername: account.username,
|
plexUsername: account.username,
|
||||||
email: account.email,
|
email: account.email,
|
||||||
@@ -441,6 +433,8 @@ router.post(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.status(201).json(User.filterMany(createdUsers));
|
return res.status(201).json(User.filterMany(createdUsers));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next({ status: 500, message: e.message });
|
next({ status: 500, message: e.message });
|
||||||
|
@@ -522,9 +522,12 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
|||||||
<span className="block ml-3">
|
<span className="block ml-3">
|
||||||
{selectedUser.displayName}
|
{selectedUser.displayName}
|
||||||
</span>
|
</span>
|
||||||
|
{selectedUser.displayName.toLowerCase() !==
|
||||||
|
selectedUser.email && (
|
||||||
<span className="ml-1 text-gray-400 truncate">
|
<span className="ml-1 text-gray-400 truncate">
|
||||||
({selectedUser.email})
|
({selectedUser.email})
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2 text-gray-500 pointer-events-none">
|
<span className="absolute inset-y-0 right-0 flex items-center pr-2 text-gray-500 pointer-events-none">
|
||||||
<ChevronDownIcon className="w-5 h-5" />
|
<ChevronDownIcon className="w-5 h-5" />
|
||||||
@@ -569,9 +572,12 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
|||||||
<span className="flex-shrink-0 block ml-3">
|
<span className="flex-shrink-0 block ml-3">
|
||||||
{user.displayName}
|
{user.displayName}
|
||||||
</span>
|
</span>
|
||||||
|
{user.displayName.toLowerCase() !==
|
||||||
|
user.email && (
|
||||||
<span className="ml-1 text-gray-400 truncate">
|
<span className="ml-1 text-gray-400 truncate">
|
||||||
({user.email})
|
({user.email})
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
{selected && (
|
{selected && (
|
||||||
<span
|
<span
|
||||||
|
@@ -602,9 +602,11 @@ const UserList: React.FC = () => {
|
|||||||
{user.displayName}
|
{user.displayName}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
{user.displayName.toLowerCase() !== user.email && (
|
||||||
<div className="text-sm leading-5 text-gray-300">
|
<div className="text-sm leading-5 text-gray-300">
|
||||||
{user.email}
|
{user.email}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Table.TD>
|
</Table.TD>
|
||||||
|
@@ -65,7 +65,7 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({
|
|||||||
{user.displayName}
|
{user.displayName}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
{user.email && (
|
{user.email && user.displayName.toLowerCase() !== user.email && (
|
||||||
<span className="text-sm text-gray-400 sm:text-lg sm:ml-2">
|
<span className="text-sm text-gray-400 sm:text-lg sm:ml-2">
|
||||||
({user.email})
|
({user.email})
|
||||||
</span>
|
</span>
|
||||||
|
@@ -188,7 +188,9 @@ const UserGeneralSettings: React.FC = () => {
|
|||||||
id="displayName"
|
id="displayName"
|
||||||
name="displayName"
|
name="displayName"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={user?.displayName}
|
placeholder={
|
||||||
|
user?.plexUsername ? user.plexUsername : user?.email
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.displayName && touched.displayName && (
|
{errors.displayName && touched.displayName && (
|
||||||
|
Reference in New Issue
Block a user