mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-30 21:14:12 +00:00 
			
		
		
		
	View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/719 Closes #764 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
		
						commit
						bcc845cdb1
					
				
					 2 changed files with 55 additions and 14 deletions
				
			
		|  | @ -55,6 +55,7 @@ export class SignupService { | ||||||
| 		host?: string | null; | 		host?: string | null; | ||||||
| 		reason?: string | null; | 		reason?: string | null; | ||||||
| 		ignorePreservedUsernames?: boolean; | 		ignorePreservedUsernames?: boolean; | ||||||
|  | 		approved?: boolean; | ||||||
| 	}) { | 	}) { | ||||||
| 		const { username, password, passwordHash, host, reason } = opts; | 		const { username, password, passwordHash, host, reason } = opts; | ||||||
| 		let hash = passwordHash; | 		let hash = passwordHash; | ||||||
|  | @ -115,9 +116,6 @@ export class SignupService { | ||||||
| 			)); | 			)); | ||||||
| 
 | 
 | ||||||
| 		let account!: MiUser; | 		let account!: MiUser; | ||||||
| 		let defaultApproval = false; |  | ||||||
| 
 |  | ||||||
| 		if (!this.meta.approvalRequiredForSignup) defaultApproval = true; |  | ||||||
| 
 | 
 | ||||||
| 		// Start transaction
 | 		// Start transaction
 | ||||||
| 		await this.db.transaction(async transactionalEntityManager => { | 		await this.db.transaction(async transactionalEntityManager => { | ||||||
|  | @ -135,7 +133,7 @@ export class SignupService { | ||||||
| 				host: this.utilityService.toPunyNullable(host), | 				host: this.utilityService.toPunyNullable(host), | ||||||
| 				token: secret, | 				token: secret, | ||||||
| 				isRoot: isTheFirstUser, | 				isRoot: isTheFirstUser, | ||||||
| 				approved: defaultApproval, | 				approved: isTheFirstUser || (opts.approved ?? !this.meta.approvalRequiredForSignup), | ||||||
| 				signupReason: reason, | 				signupReason: reason, | ||||||
| 			})); | 			})); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,16 +3,16 @@ | ||||||
|  * SPDX-License-Identifier: AGPL-3.0-only |  * SPDX-License-Identifier: AGPL-3.0-only | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||||
| import { IsNull } from 'typeorm'; |  | ||||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||||
| import type { UsersRepository } from '@/models/_.js'; | import { MiAccessToken, MiUser } from '@/models/_.js'; | ||||||
| import { SignupService } from '@/core/SignupService.js'; | import { SignupService } from '@/core/SignupService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { InstanceActorService } from '@/core/InstanceActorService.js'; | import { InstanceActorService } from '@/core/InstanceActorService.js'; | ||||||
| import { localUsernameSchema, passwordSchema } from '@/models/User.js'; | import { localUsernameSchema, passwordSchema } from '@/models/User.js'; | ||||||
| import { DI } from '@/di-symbols.js'; |  | ||||||
| import { Packed } from '@/misc/json-schema.js'; | import { Packed } from '@/misc/json-schema.js'; | ||||||
|  | import { RoleService } from '@/core/RoleService.js'; | ||||||
|  | import { ApiError } from '@/server/api/error.js'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
| 	tags: ['admin'], | 	tags: ['admin'], | ||||||
|  | @ -28,6 +28,35 @@ export const meta = { | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
|  | 
 | ||||||
|  | 	errors: { | ||||||
|  | 		// From ApiCallService.ts
 | ||||||
|  | 		noCredential: { | ||||||
|  | 			message: 'Credential required.', | ||||||
|  | 			code: 'CREDENTIAL_REQUIRED', | ||||||
|  | 			id: '1384574d-a912-4b81-8601-c7b1c4085df1', | ||||||
|  | 			httpStatusCode: 401, | ||||||
|  | 		}, | ||||||
|  | 		noAdmin: { | ||||||
|  | 			message: 'You are not assigned to an administrator role.', | ||||||
|  | 			code: 'ROLE_PERMISSION_DENIED', | ||||||
|  | 			kind: 'permission', | ||||||
|  | 			id: 'c3d38592-54c0-429d-be96-5636b0431a61', | ||||||
|  | 		}, | ||||||
|  | 		noPermission: { | ||||||
|  | 			message: 'Your app does not have the necessary permissions to use this endpoint.', | ||||||
|  | 			code: 'PERMISSION_DENIED', | ||||||
|  | 			kind: 'permission', | ||||||
|  | 			id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	// Required token permissions, but we need to check them manually.
 | ||||||
|  | 	// ApiCallService checks access in a way that would prevent creating the first account.
 | ||||||
|  | 	softPermissions: [ | ||||||
|  | 		'write:admin:account', | ||||||
|  | 		'write:admin:approve-user', | ||||||
|  | 	], | ||||||
| } as const; | } as const; | ||||||
| 
 | 
 | ||||||
| export const paramDef = { | export const paramDef = { | ||||||
|  | @ -42,22 +71,19 @@ export const paramDef = { | ||||||
| @Injectable() | @Injectable() | ||||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | ||||||
| 	constructor( | 	constructor( | ||||||
| 		@Inject(DI.usersRepository) | 		private roleService: RoleService, | ||||||
| 		private usersRepository: UsersRepository, |  | ||||||
| 
 |  | ||||||
| 		private userEntityService: UserEntityService, | 		private userEntityService: UserEntityService, | ||||||
| 		private signupService: SignupService, | 		private signupService: SignupService, | ||||||
| 		private instanceActorService: InstanceActorService, | 		private instanceActorService: InstanceActorService, | ||||||
| 	) { | 	) { | ||||||
| 		super(meta, paramDef, async (ps, _me, token) => { | 		super(meta, paramDef, async (ps, _me, token) => { | ||||||
| 			const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; | 			await this.ensurePermissions(_me, token); | ||||||
| 			const realUsers = await this.instanceActorService.realLocalUsersPresent(); |  | ||||||
| 			if ((realUsers && !me?.isRoot) || token !== null) throw new Error('access denied'); |  | ||||||
| 
 | 
 | ||||||
| 			const { account, secret } = await this.signupService.signup({ | 			const { account, secret } = await this.signupService.signup({ | ||||||
| 				username: ps.username, | 				username: ps.username, | ||||||
| 				password: ps.password, | 				password: ps.password, | ||||||
| 				ignorePreservedUsernames: true, | 				ignorePreservedUsernames: true, | ||||||
|  | 				approved: true, | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			const res = await this.userEntityService.pack(account, account, { | 			const res = await this.userEntityService.pack(account, account, { | ||||||
|  | @ -70,4 +96,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||||
| 			return res; | 			return res; | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	private async ensurePermissions(me: MiUser | null, token: MiAccessToken | null): Promise<void> { | ||||||
|  | 		// Tokens have scoped permissions which may be *less* than the user's official role, so we need to check.
 | ||||||
|  | 		if (token && !meta.softPermissions.every(p => token.permission.includes(p))) { | ||||||
|  | 			throw new ApiError(meta.errors.noPermission); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Only administrators (including root) can create users.
 | ||||||
|  | 		if (me && !await this.roleService.isAdministrator(me)) { | ||||||
|  | 			throw new ApiError(meta.errors.noAdmin); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Anonymous access is only allowed for initial instance setup.
 | ||||||
|  | 		if (!me && await this.instanceActorService.realLocalUsersPresent()) { | ||||||
|  | 			throw new ApiError(meta.errors.noCredential); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue