Skip to content

Commit 2abe381

Browse files
fix: team check in edit user (#1013)
Co-authored-by: svcAPLBot <174728082+svcAPLBot@users.noreply.github.com>
1 parent 0803b4b commit 2abe381

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

src/otomi-stack.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from 'src/otomi-models'
1010
import OtomiStack from 'src/otomi-stack'
1111
import { loadSpec } from './app'
12-
import { ValidationError } from './error'
12+
import { NotExistError, ValidationError } from './error'
1313
import { Git } from './git'
1414

1515
jest.mock('./tty', () => ({
@@ -621,6 +621,60 @@ describe('Users tests', () => {
621621
})
622622
})
623623

624+
it('should not allow editing a user into a team that does not exist', async () => {
625+
createTestTeam(otomiStack, 'team1')
626+
627+
const user = {
628+
...teamMember1,
629+
id: 'missing-team-user',
630+
email: 'missing-team-user@dev.linode-apl.net',
631+
teams: ['team1'],
632+
}
633+
634+
createTestUser(otomiStack, user)
635+
636+
await expect(
637+
otomiStack.editUser(
638+
user.id!,
639+
{
640+
...user,
641+
teams: ['team1', 'team-does-not-exist'],
642+
},
643+
platformAdminSession,
644+
),
645+
).rejects.toThrow(new NotExistError('Team(s) not found: team-does-not-exist'))
646+
647+
expect(mockGit.writeTextFile).not.toHaveBeenCalled()
648+
expect(otomiStack.doDeployment).not.toHaveBeenCalled()
649+
})
650+
651+
it('should allow editing a user into existing teams', async () => {
652+
createTestTeam(otomiStack, 'team1')
653+
createTestTeam(otomiStack, 'team2')
654+
655+
const user = {
656+
...teamMember1,
657+
id: 'existing-team-user',
658+
email: 'existing-team-user@dev.linode-apl.net',
659+
teams: ['team1'],
660+
}
661+
662+
createTestUser(otomiStack, user)
663+
664+
const result = await otomiStack.editUser(
665+
user.id!,
666+
{
667+
...user,
668+
teams: ['team1', 'team2'],
669+
},
670+
platformAdminSession,
671+
)
672+
673+
expect(result.teams).toEqual(['team1', 'team2'])
674+
expect(mockGit.writeTextFile).toHaveBeenCalled()
675+
expect(otomiStack.doDeployment).toHaveBeenCalled()
676+
})
677+
624678
describe('canTeamAdminUpdateUserTeams', () => {
625679
// set session user as team admin
626680
sessionUser.isPlatformAdmin = false

src/otomi-stack.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,21 +1218,35 @@ export default class OtomiStack {
12181218
if (!existingData) {
12191219
throw new NotExistError(`User ${id} not found`)
12201220
}
1221+
12211222
const existingUser = userSecretDataToUser(existingData)
12221223

1223-
// Merge updates, preserving initialPassword from existing secret
12241224
const user: User = {
12251225
...existingUser,
12261226
...data,
12271227
id,
12281228
initialPassword: existingUser.initialPassword,
12291229
}
12301230

1231+
this.validateUserTeamsExist(user)
1232+
12311233
const aplRecord = await this.saveUser(user)
12321234
await this.doDeployment(aplRecord)
1235+
12331236
return user
12341237
}
12351238

1239+
private validateUserTeamsExist(user: User): void {
1240+
const existingTeamIds = new Set(this.getTeamIds())
1241+
const userTeamIds = user.teams ?? []
1242+
1243+
const missingTeamIds = userTeamIds.filter((teamId) => !existingTeamIds.has(teamId))
1244+
1245+
if (missingTeamIds.length > 0) {
1246+
throw new NotExistError(`Team(s) not found: ${missingTeamIds.join(', ')}`)
1247+
}
1248+
}
1249+
12361250
async deleteUser(id: string): Promise<void> {
12371251
const existingData = await getUserSecretData(id, this.fileStore)
12381252
if (!existingData) {

0 commit comments

Comments
 (0)