4
4
* See License.AGPL.txt in the project root for license information.
5
5
*/
6
6
7
+ import { list as blocklist } from "the-big-username-blacklist" ;
7
8
import { Team , TeamMemberInfo , TeamMemberRole , TeamMembershipInvite , User } from "@gitpod/gitpod-protocol" ;
8
9
import { inject , injectable } from "inversify" ;
9
10
import { TypeORM } from "./typeorm" ;
10
11
import { Repository } from "typeorm" ;
11
12
import { v4 as uuidv4 } from "uuid" ;
13
+ import { randomBytes } from "crypto" ;
12
14
import { TeamDB } from "../team-db" ;
13
15
import { DBTeam } from "./entity/db-team" ;
14
16
import { DBTeamMembership } from "./entity/db-team-membership" ;
@@ -121,17 +123,37 @@ export class TeamDBImpl implements TeamDB {
121
123
return soleOwnedTeams ;
122
124
}
123
125
124
- public async updateTeam ( teamId : string , team : Pick < Team , "name" > ) : Promise < Team > {
126
+ public async updateTeam ( teamId : string , team : Pick < Team , "name" | "slug" > ) : Promise < Team > {
127
+ const name = team . name && team . name . trim ( ) ;
128
+ const slug = team . slug && team . slug . trim ( ) ;
129
+ if ( ! name && ! slug ) {
130
+ throw new ResponseError ( ErrorCodes . BAD_REQUEST , "No update provided" ) ;
131
+ }
132
+
125
133
const teamRepo = await this . getTeamRepo ( ) ;
126
134
const existingTeam = await teamRepo . findOne ( { id : teamId , deleted : false , markedDeleted : false } ) ;
127
135
if ( ! existingTeam ) {
128
136
throw new ResponseError ( ErrorCodes . NOT_FOUND , "Organization not found" ) ;
129
137
}
130
- const name = team . name && team . name . trim ( ) ;
131
- if ( ! name || name . length === 0 || name . length > 32 ) {
132
- throw new ResponseError ( ErrorCodes . INVALID_VALUE , "The name must be between 1 and 32 characters long" ) ;
138
+
139
+ // no changes
140
+ if ( existingTeam . name === name && existingTeam . slug === slug ) {
141
+ return existingTeam ;
142
+ }
143
+
144
+ if ( ! ! name ) {
145
+ if ( name . length > 32 ) {
146
+ throw new ResponseError ( ErrorCodes . INVALID_VALUE , "The name must be between 1 and 32 characters long" ) ;
147
+ }
148
+ existingTeam . name = name ;
149
+ }
150
+ if ( ! ! slug ) {
151
+ if ( slug . length > 100 ) {
152
+ throw new ResponseError ( ErrorCodes . INVALID_VALUE , "Slug must be between 1 and 100 characters long" ) ;
153
+ }
154
+ existingTeam . slug = slug ;
133
155
}
134
- existingTeam . name = name ;
156
+
135
157
return teamRepo . save ( existingTeam ) ;
136
158
}
137
159
@@ -145,8 +167,17 @@ export class TeamDBImpl implements TeamDB {
145
167
"Please choose a name containing only letters, numbers, -, _, ', or spaces." ,
146
168
) ;
147
169
}
170
+ const slug = name . toLocaleLowerCase ( ) . replace ( / [ ^ a - z A - Z 0 - 9 ] + / g, "-" ) + "-" + randomBytes ( 10 ) . toString ( "hex" ) ;
171
+ if ( blocklist . indexOf ( slug ) !== - 1 ) {
172
+ throw new ResponseError ( ErrorCodes . BAD_REQUEST , "Creating a team with this name is not allowed" ) ;
173
+ }
148
174
149
175
const teamRepo = await this . getTeamRepo ( ) ;
176
+ const existingTeam = await teamRepo . findOne ( { slug, deleted : false , markedDeleted : false } ) ;
177
+ if ( ! ! existingTeam ) {
178
+ throw new ResponseError ( ErrorCodes . CONFLICT , "A team with this name already exists" ) ;
179
+ }
180
+
150
181
const team : Team = {
151
182
id : uuidv4 ( ) ,
152
183
name,
0 commit comments