From a9bfa85d2cdf13670aaced740da5b493fbeddfce Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 15 Dec 2021 15:58:10 +0100 Subject: Add ability for admins to set default p2p policy --- client/src/app/core/auth/auth-user.model.ts | 44 +---- client/src/app/core/auth/auth.service.ts | 16 +- client/src/app/core/core.module.ts | 3 +- client/src/app/core/users/index.ts | 1 + .../app/core/users/user-local-storage.service.ts | 186 +++++++++++++++++++++ client/src/app/core/users/user.model.ts | 8 +- client/src/app/core/users/user.service.ts | 104 +++--------- 7 files changed, 226 insertions(+), 136 deletions(-) create mode 100644 client/src/app/core/users/user-local-storage.service.ts (limited to 'client/src/app/core') diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index f10b37e5a..cd9665e37 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts @@ -1,13 +1,7 @@ import { Observable, of } from 'rxjs' import { map } from 'rxjs/operators' import { User } from '@app/core/users/user.model' -import { - flushUserInfoFromLocalStorage, - getUserInfoFromLocalStorage, - saveUserInfoIntoLocalStorage, - TokenOptions, - Tokens -} from '@root-helpers/users' +import { UserTokens } from '@root-helpers/users' import { hasUserRight } from '@shared/core-utils/users' import { MyUser as ServerMyUserModel, @@ -19,31 +13,15 @@ import { } from '@shared/models' export class AuthUser extends User implements ServerMyUserModel { - tokens: Tokens + tokens: UserTokens specialPlaylists: MyUserSpecialPlaylist[] canSeeVideosLink = true - static load () { - const tokens = Tokens.load() - if (!tokens) return null - - const userInfo = getUserInfoFromLocalStorage() - if (!userInfo) return null - - return new AuthUser(userInfo, tokens) - } - - static flush () { - flushUserInfoFromLocalStorage() - - Tokens.flush() - } - - constructor (userHash: Partial, hashTokens: TokenOptions) { + constructor (userHash: Partial, hashTokens: Partial) { super(userHash) - this.tokens = new Tokens(hashTokens) + this.tokens = new UserTokens(hashTokens) this.specialPlaylists = userHash.specialPlaylists } @@ -77,20 +55,6 @@ export class AuthUser extends User implements ServerMyUserModel { return user.role === UserRole.USER } - save () { - saveUserInfoIntoLocalStorage({ - id: this.id, - username: this.username, - email: this.email, - role: this.role, - nsfwPolicy: this.nsfwPolicy, - webTorrentEnabled: this.webTorrentEnabled, - autoPlayVideo: this.autoPlayVideo - }) - - this.tokens.save() - } - computeCanSeeVideosLink (quotaObservable: Observable): Observable { if (!this.isUploadDisabled()) { this.canSeeVideosLink = true diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 79239a17a..2ac88c185 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -5,7 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { Router } from '@angular/router' import { Notifier } from '@app/core/notification/notifier.service' -import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' +import { objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index' import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' import { environment } from '../../../environments/environment' import { RestExtractor } from '../rest/rest-extractor.service' @@ -34,6 +34,7 @@ export class AuthService { loginChangedSource: Observable userInformationLoaded = new ReplaySubject(1) + tokensRefreshed = new ReplaySubject(1) hotkeys: Hotkey[] private clientId: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) @@ -52,9 +53,6 @@ export class AuthService { this.loginChanged = new Subject() this.loginChangedSource = this.loginChanged.asObservable() - // Return null if there is nothing to load - this.user = AuthUser.load() - // Set HotKeys this.hotkeys = [ new Hotkey('m s', (event: KeyboardEvent): boolean => { @@ -76,6 +74,10 @@ export class AuthService { ] } + buildAuthUser (userInfo: Partial, tokens: UserTokens) { + this.user = new AuthUser(userInfo, tokens) + } + loadClientCredentials () { // Fetch the client_id/client_secret this.http.get(AuthService.BASE_CLIENT_URL) @@ -180,8 +182,6 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular this.user = null - AuthUser.flush() - this.setStatus(AuthStatus.LoggedOut) this.hotkeysService.remove(this.hotkeys) @@ -239,7 +239,6 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular .subscribe({ next: res => { this.user.patch(res) - this.user.save() this.userInformationLoaded.next(true) } @@ -262,7 +261,6 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular } this.user = new AuthUser(obj, hashTokens) - this.user.save() this.setStatus(AuthStatus.LoggedIn) this.userInformationLoaded.next(true) @@ -272,7 +270,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular private handleRefreshToken (obj: UserRefreshToken) { this.user.refreshTokens(obj.access_token, obj.refresh_token) - this.user.save() + this.tokensRefreshed.next() } private setStatus (status: AuthStatus) { diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 04be0671c..d80f95ed6 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -30,7 +30,7 @@ import { ServerConfigResolver } from './routing/server-config-resolver.service' import { ScopedTokensService } from './scoped-tokens' import { ServerService } from './server' import { ThemeService } from './theme' -import { UserService } from './users' +import { UserLocalStorageService, UserService } from './users' import { LocalStorageService, ScreenService, SessionStorageService } from './wrappers' @NgModule({ @@ -79,6 +79,7 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra RestService, UserService, + UserLocalStorageService, ScreenService, LocalStorageService, diff --git a/client/src/app/core/users/index.ts b/client/src/app/core/users/index.ts index 7b5a67bc7..e235a875b 100644 --- a/client/src/app/core/users/index.ts +++ b/client/src/app/core/users/index.ts @@ -1,2 +1,3 @@ +export * from './user-local-storage.service' export * from './user.model' export * from './user.service' diff --git a/client/src/app/core/users/user-local-storage.service.ts b/client/src/app/core/users/user-local-storage.service.ts new file mode 100644 index 000000000..85da46e0d --- /dev/null +++ b/client/src/app/core/users/user-local-storage.service.ts @@ -0,0 +1,186 @@ + +import { filter, throttleTime } from 'rxjs' +import { Injectable } from '@angular/core' +import { AuthService, AuthStatus } from '@app/core/auth' +import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users' +import { getBoolOrDefault } from '@root-helpers/local-storage-utils' +import { UserRole, UserUpdateMe } from '@shared/models' +import { NSFWPolicyType } from '@shared/models/videos' +import { ServerService } from '../server' +import { LocalStorageService } from '../wrappers/storage.service' + +@Injectable() +export class UserLocalStorageService { + + constructor ( + private authService: AuthService, + private server: ServerService, + private localStorageService: LocalStorageService + ) { + this.authService.userInformationLoaded.subscribe({ + next: () => { + const user = this.authService.getUser() + + this.setLoggedInUser(user) + this.setUserInfo(user) + this.setTokens(user.tokens) + } + }) + + this.authService.loginChangedSource + .pipe(filter(status => status === AuthStatus.LoggedOut)) + .subscribe({ + next: () => { + this.flushLoggedInUser() + this.flushUserInfo() + this.flushTokens() + } + }) + + this.authService.tokensRefreshed + .subscribe({ + next: () => { + const user = this.authService.getUser() + + this.setTokens(user.tokens) + } + }) + } + + // --------------------------------------------------------------------------- + + getLoggedInUser () { + const usernameLocalStorage = this.localStorageService.getItem(UserLocalStorageKeys.USERNAME) + + if (!usernameLocalStorage) return undefined + + return { + id: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ID), 10), + username: this.localStorageService.getItem(UserLocalStorageKeys.USERNAME), + email: this.localStorageService.getItem(UserLocalStorageKeys.EMAIL), + role: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ROLE), 10) as UserRole, + + ...this.getUserInfo() + } + } + + setLoggedInUser (user: { + id: number + username: string + email: string + role: UserRole + }) { + this.localStorageService.setItem(UserLocalStorageKeys.ID, user.id.toString()) + this.localStorageService.setItem(UserLocalStorageKeys.USERNAME, user.username) + this.localStorageService.setItem(UserLocalStorageKeys.EMAIL, user.email) + this.localStorageService.setItem(UserLocalStorageKeys.ROLE, user.role.toString()) + } + + flushLoggedInUser () { + this.localStorageService.removeItem(UserLocalStorageKeys.ID) + this.localStorageService.removeItem(UserLocalStorageKeys.USERNAME) + this.localStorageService.removeItem(UserLocalStorageKeys.EMAIL) + this.localStorageService.removeItem(UserLocalStorageKeys.ROLE) + } + + // --------------------------------------------------------------------------- + + getUserInfo () { + let videoLanguages: string[] + + try { + const languagesString = this.localStorageService.getItem(UserLocalStorageKeys.VIDEO_LANGUAGES) + videoLanguages = languagesString && languagesString !== 'undefined' + ? JSON.parse(languagesString) + : null + } catch (err) { + videoLanguages = null + console.error('Cannot parse desired video languages from localStorage.', err) + } + + const htmlConfig = this.server.getHTMLConfig() + + const defaultNSFWPolicy = htmlConfig.instance.defaultNSFWPolicy + const defaultP2PEnabled = htmlConfig.defaults.p2p.enabled + + return { + nsfwPolicy: this.localStorageService.getItem(UserLocalStorageKeys.NSFW_POLICY) || defaultNSFWPolicy, + p2pEnabled: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.P2P_ENABLED), defaultP2PEnabled), + theme: this.localStorageService.getItem(UserLocalStorageKeys.THEME) || 'instance-default', + videoLanguages, + + autoPlayVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO), true), + autoPlayNextVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_NEXT_VIDEO), false), + autoPlayNextVideoPlaylist: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST), true) + } + } + + setUserInfo (profile: UserUpdateMe) { + const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { + nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, + p2pEnabled: UserLocalStorageKeys.P2P_ENABLED, + autoPlayNextVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, + autoPlayNextVideoPlaylist: UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST, + theme: UserLocalStorageKeys.THEME, + videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES + } + + const obj = Object.keys(localStorageKeys) + .filter(key => key in profile) + .map(key => ([ localStorageKeys[key], profile[key] ])) + + for (const [ key, value ] of obj) { + try { + if (value === undefined) { + this.localStorageService.removeItem(key) + continue + } + + const localStorageValue = typeof value === 'string' + ? value + : JSON.stringify(value) + + this.localStorageService.setItem(key, localStorageValue) + } catch (err) { + console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err) + } + } + } + + flushUserInfo () { + this.localStorageService.removeItem(UserLocalStorageKeys.NSFW_POLICY) + this.localStorageService.removeItem(UserLocalStorageKeys.P2P_ENABLED) + this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO) + this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST) + this.localStorageService.removeItem(UserLocalStorageKeys.THEME) + this.localStorageService.removeItem(UserLocalStorageKeys.VIDEO_LANGUAGES) + } + + listenUserInfoChange () { + return this.localStorageService.watch([ + UserLocalStorageKeys.NSFW_POLICY, + UserLocalStorageKeys.P2P_ENABLED, + UserLocalStorageKeys.AUTO_PLAY_VIDEO, + UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST, + UserLocalStorageKeys.THEME, + UserLocalStorageKeys.VIDEO_LANGUAGES + ]).pipe( + throttleTime(200), + filter(() => this.authService.isLoggedIn() !== true) + ) + } + + // --------------------------------------------------------------------------- + + getTokens () { + return UserTokens.getUserTokens(this.localStorageService) + } + + setTokens (tokens: UserTokens) { + UserTokens.saveToLocalStorage(this.localStorageService, tokens) + } + + flushTokens () { + UserTokens.flushLocalStorage(this.localStorageService) + } +} diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index c0e5d3169..f211051ce 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts @@ -26,7 +26,11 @@ export class User implements UserServerModel { autoPlayVideo: boolean autoPlayNextVideo: boolean autoPlayNextVideoPlaylist: boolean - webTorrentEnabled: boolean + + p2pEnabled: boolean + // FIXME: deprecated in 4.1 + webTorrentEnabled: never + videosHistoryEnabled: boolean videoLanguages: string[] @@ -84,7 +88,7 @@ export class User implements UserServerModel { this.videoCommentsCount = hash.videoCommentsCount this.nsfwPolicy = hash.nsfwPolicy - this.webTorrentEnabled = hash.webTorrentEnabled + this.p2pEnabled = hash.p2pEnabled this.autoPlayVideo = hash.autoPlayVideo this.autoPlayNextVideo = hash.autoPlayNextVideo this.autoPlayNextVideoPlaylist = hash.autoPlayNextVideoPlaylist diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts index 632361e9b..a6a0474ab 100644 --- a/client/src/app/core/users/user.service.ts +++ b/client/src/app/core/users/user.service.ts @@ -1,11 +1,10 @@ import { SortMeta } from 'primeng/api' import { from, Observable, of } from 'rxjs' -import { catchError, concatMap, filter, first, map, shareReplay, tap, throttleTime, toArray } from 'rxjs/operators' +import { catchError, concatMap, first, map, shareReplay, tap, toArray } from 'rxjs/operators' import { HttpClient, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { AuthService } from '@app/core/auth' import { getBytes } from '@root-helpers/bytes' -import { UserLocalStorageKeys } from '@root-helpers/users' import { ActorImage, ResultList, @@ -17,10 +16,9 @@ import { UserUpdateMe, UserVideoQuota } from '@shared/models' -import { ServerService } from '../' import { environment } from '../../../environments/environment' import { RestExtractor, RestPagination, RestService } from '../rest' -import { LocalStorageService, SessionStorageService } from '../wrappers/storage.service' +import { UserLocalStorageService } from './' import { User } from './user.model' @Injectable() @@ -33,12 +31,10 @@ export class UserService { constructor ( private authHttp: HttpClient, - private server: ServerService, private authService: AuthService, private restExtractor: RestExtractor, private restService: RestService, - private localStorageService: LocalStorageService, - private sessionStorageService: SessionStorageService + private userLocalStorageService: UserLocalStorageService ) { } hasSignupInThisSession () { @@ -73,6 +69,23 @@ export class UserService { ) } + // --------------------------------------------------------------------------- + + updateMyAnonymousProfile (profile: UserUpdateMe) { + this.userLocalStorageService.setUserInfo(profile) + } + + listenAnonymousUpdate () { + return this.userLocalStorageService.listenUserInfoChange() + .pipe(map(() => this.getAnonymousUser())) + } + + getAnonymousUser () { + return new User(this.userLocalStorageService.getUserInfo()) + } + + // --------------------------------------------------------------------------- + updateMyProfile (profile: UserUpdateMe) { const url = UserService.BASE_USERS_URL + 'me' @@ -83,53 +96,6 @@ export class UserService { ) } - updateMyAnonymousProfile (profile: UserUpdateMe) { - const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { - nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, - webTorrentEnabled: UserLocalStorageKeys.WEBTORRENT_ENABLED, - autoPlayNextVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, - autoPlayNextVideoPlaylist: UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST, - theme: UserLocalStorageKeys.THEME, - videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES - } - - const obj = Object.keys(localStorageKeys) - .filter(key => key in profile) - .map(key => ([ localStorageKeys[key], profile[key] ])) - - for (const [ key, value ] of obj) { - try { - if (value === undefined) { - this.localStorageService.removeItem(key) - continue - } - - const localStorageValue = typeof value === 'string' - ? value - : JSON.stringify(value) - - this.localStorageService.setItem(key, localStorageValue) - } catch (err) { - console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err) - } - } - } - - listenAnonymousUpdate () { - return this.localStorageService.watch([ - UserLocalStorageKeys.NSFW_POLICY, - UserLocalStorageKeys.WEBTORRENT_ENABLED, - UserLocalStorageKeys.AUTO_PLAY_VIDEO, - UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST, - UserLocalStorageKeys.THEME, - UserLocalStorageKeys.VIDEO_LANGUAGES - ]).pipe( - throttleTime(200), - filter(() => this.authService.isLoggedIn() !== true), - map(() => this.getAnonymousUser()) - ) - } - deleteMe () { const url = UserService.BASE_USERS_URL + 'me' @@ -287,36 +253,6 @@ export class UserService { .pipe(catchError(err => this.restExtractor.handleError(err))) } - getAnonymousUser () { - let videoLanguages: string[] - - try { - const languagesString = this.localStorageService.getItem(UserLocalStorageKeys.VIDEO_LANGUAGES) - videoLanguages = languagesString && languagesString !== 'undefined' - ? JSON.parse(languagesString) - : null - } catch (err) { - videoLanguages = null - console.error('Cannot parse desired video languages from localStorage.', err) - } - - const defaultNSFWPolicy = this.server.getHTMLConfig().instance.defaultNSFWPolicy - - return new User({ - // local storage keys - nsfwPolicy: this.localStorageService.getItem(UserLocalStorageKeys.NSFW_POLICY) || defaultNSFWPolicy, - webTorrentEnabled: this.localStorageService.getItem(UserLocalStorageKeys.WEBTORRENT_ENABLED) !== 'false', - theme: this.localStorageService.getItem(UserLocalStorageKeys.THEME) || 'instance-default', - videoLanguages, - - autoPlayNextVideoPlaylist: this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST) !== 'false', - autoPlayVideo: this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO) === 'true', - - // session storage keys - autoPlayNextVideo: this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true' - }) - } - getUsers (parameters: { pagination: RestPagination sort: SortMeta -- cgit v1.2.3