@@ -24,6 +24,11 @@ import { buildProjection, Time } from '../utils';
2424import { ContestDetailBaseHandler } from './contest' ;
2525import { postJudge } from './judge' ;
2626
27+ // FIXME: move this
28+ const isOwnOrTeammateRecord = async ( domainId : string , rdoc : RecordDoc , uid : number ) => (
29+ rdoc . uid === uid || ( ! ! rdoc . contest && await contest . isSameTeam ( domainId , rdoc . contest , rdoc . uid , uid ) )
30+ ) ;
31+
2732export class RecordListHandler extends ContestDetailBaseHandler {
2833 @param ( 'page' , Types . PositiveInt , true )
2934 @param ( 'pid' , Types . ProblemId , true )
@@ -53,16 +58,23 @@ export class RecordListHandler extends ContestDetailBaseHandler {
5358 if ( udoc ) q . uid = udoc . _id ;
5459 else invalid = true ;
5560 }
56- if ( q . uid !== this . user . _id ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
61+ // Team: a member viewing the contest record list sees the whole team's submissions.
62+ let teamMembers : number [ ] | null = null ;
63+ if ( tid && this . team && ( q . uid === undefined || q . uid === this . user . _id ) ) {
64+ // this.tsdoc is already the team vuser's tsdoc (swapped in __prepare); no extra DB fetch needed
65+ teamMembers = this . tsdoc ?. members ?. length ? this . tsdoc . members : null ;
66+ }
67+ if ( q . uid !== this . user . _id && ! teamMembers ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
5768 if ( tid ) {
5869 tdoc = await contest . get ( domainId , tid ) ;
5970 this . tdoc = tdoc ;
6071 if ( ! tdoc ) throw new ContestNotFoundError ( domainId , pid ) ;
6172 if ( ! contest . canShowScoreboard . call ( this , tdoc , true ) ) throw new PermissionError ( PERM . PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD ) ;
62- if ( ! contest [ q . uid === this . user . _id ? 'canShowSelfRecord' : 'canShowRecord' ] . call ( this , tdoc , true ) ) {
73+ const viewingSelf = q . uid === this . user . _id || ! ! teamMembers ;
74+ if ( ! contest [ viewingSelf ? 'canShowSelfRecord' : 'canShowRecord' ] . call ( this , tdoc , true ) ) {
6375 throw new PermissionError ( PERM . PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD ) ;
6476 }
65- if ( ! ( await contest . getStatus ( domainId , tid , this . user . _id ) ) ?. attend ) {
77+ if ( ! this . team && ! ( await contest . getStatus ( domainId , tid , this . user . _id ) ) ?. attend ) {
6678 const name = tdoc . rule === 'homework'
6779 ? "You haven't claimed this homework yet."
6880 : "You haven't attended this contest yet." ;
@@ -89,6 +101,7 @@ export class RecordListHandler extends ContestDetailBaseHandler {
89101 delete q . contest ;
90102 q . _id = { $gt : Time . getObjectID ( new Date ( Date . now ( ) - 10 * Time . week ) ) } ;
91103 }
104+ if ( teamMembers && ! all && ! allDomain ) q . uid = { $in : teamMembers } ;
92105 let cursor = record . getMulti ( allDomain ? '' : domainId , q ) . sort ( '_id' , - 1 ) ;
93106 if ( ! full ) cursor = cursor . project ( buildProjection ( record . PROJECTION_LIST ) ) ;
94107 const limit = full ? 10 : system . get ( 'pagination.record' ) ;
@@ -136,7 +149,7 @@ export class RecordDetailHandler extends ContestDetailBaseHandler {
136149 async prepare ( domainId : string , rid : ObjectId ) {
137150 this . rdoc = await record . get ( domainId , rid ) ;
138151 if ( ! this . rdoc ) throw new RecordNotFoundError ( rid ) ;
139- if ( this . rdoc . uid !== this . user . _id ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
152+ if ( ! ( await isOwnOrTeammateRecord ( domainId , this . rdoc , this . user . _id ) ) ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
140153 }
141154
142155 async download ( ) {
@@ -171,8 +184,8 @@ export class RecordDetailHandler extends ContestDetailBaseHandler {
171184 this . tdoc = await contest . get ( domainId , rdoc . contest ) ;
172185 let canView = this . user . own ( this . tdoc ) ;
173186 canView ||= contest . canShowRecord . call ( this , this . tdoc ) ;
174- canView ||= contest . canShowSelfRecord . call ( this , this . tdoc , true ) && rdoc . uid === this . user . _id ;
175- if ( ! canView && rdoc . uid !== this . user . _id ) throw new PermissionError ( rid ) ;
187+ canView ||= contest . canShowSelfRecord . call ( this , this . tdoc , true ) && await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ;
188+ if ( ! canView && ! ( await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ) ) throw new PermissionError ( rid ) ;
176189 canViewDetail = canView ;
177190 this . args . tid = this . tdoc . docId ;
178191 if ( ! this . user . own ( this . tdoc ) && ! this . user . hasPerm ( PERM . PERM_EDIT_CONTEST ) ) {
@@ -187,12 +200,13 @@ export class RecordDetailHandler extends ContestDetailBaseHandler {
187200 user . getById ( domainId , rdoc . uid ) ,
188201 ] ) ;
189202
190- let canViewCode = rdoc . uid === this . user . _id ;
203+ let canViewCode = await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ;
191204 canViewCode ||= this . user . hasPriv ( PRIV . PRIV_READ_RECORD_CODE ) ;
192205 canViewCode ||= this . user . hasPerm ( PERM . PERM_READ_RECORD_CODE ) ;
193206 canViewCode ||= this . user . hasPerm ( PERM . PERM_READ_RECORD_CODE_ACCEPT ) && self ?. status === STATUS . STATUS_ACCEPTED ;
194207 if ( this . tdoc ) {
195- this . tsdoc = await contest . getStatus ( domainId , this . tdoc . docId , this . user . _id ) ;
208+ const teamVuser = this . tdoc . allowTeam ? await contest . getTeamVuser ( domainId , this . tdoc . docId , this . user . _id ) : null ;
209+ this . tsdoc = await contest . getStatus ( domainId , this . tdoc . docId , teamVuser ?? this . user . _id ) ;
196210 canViewCode ||= this . user . own ( this . tdoc ) ;
197211 if ( this . tdoc . allowViewCode && contest . isDone ( this . tdoc ) ) {
198212 canViewCode ||= ! ! this . tsdoc ?. attend ;
@@ -299,7 +313,12 @@ export class RecordMainConnectionHandler extends ConnectionHandler {
299313 else throw new UserNotFoundError ( uidOrName ) ;
300314 }
301315 }
302- if ( this . uid !== this . user . _id ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
316+ if ( this . uid !== this . user . _id ) {
317+ const tdoc = this . tdoc ;
318+ const sameTeam = ! ! tdoc && tdoc . allowTeam && typeof this . uid === 'number'
319+ && await contest . isSameTeam ( domainId , tdoc . docId , this . uid , this . user . _id ) ;
320+ if ( ! sameTeam ) this . checkPerm ( PERM . PERM_VIEW_RECORD ) ;
321+ }
303322 if ( pid ) {
304323 const pdoc = await problem . get ( domainId , pid ) ;
305324 if ( pdoc ) this . pid = pdoc . docId ;
@@ -336,8 +355,9 @@ export class RecordMainConnectionHandler extends ConnectionHandler {
336355 if ( ! rdoc . contest && this . tid ) return ;
337356 if ( rdoc . contest && ! [ this . tid , '000000000000000000000000' ] . includes ( rdoc . contest . toString ( ) ) ) return ;
338357 if ( this . tid && rdoc . contest ?. toString ( ) !== '0' . repeat ( 24 ) ) {
339- if ( rdoc . uid !== this . user . _id && ! contest . canShowRecord . call ( this , this . tdoc , true ) ) return ;
340- if ( rdoc . uid === this . user . _id && ! contest . canShowSelfRecord . call ( this , this . tdoc , true ) ) return ;
358+ const own = await isOwnOrTeammateRecord ( this . args . domainId , rdoc , this . user . _id ) ;
359+ if ( ! own && ! contest . canShowRecord . call ( this , this . tdoc , true ) ) return ;
360+ if ( own && ! contest . canShowSelfRecord . call ( this , this . tdoc , true ) ) return ;
341361 }
342362 }
343363 }
@@ -397,7 +417,7 @@ export class RecordDetailConnectionHandler extends ConnectionHandler {
397417 this . tdoc = await contest . get ( domainId , rdoc . contest ) ;
398418 let canView = this . user . own ( this . tdoc ) ;
399419 canView ||= contest . canShowRecord . call ( this , this . tdoc ) ;
400- canView ||= this . user . _id === rdoc . uid && contest . canShowSelfRecord . call ( this , this . tdoc ) ;
420+ canView ||= ( await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ) && contest . canShowSelfRecord . call ( this , this . tdoc ) ;
401421 if ( ! canView ) throw new PermissionError ( PERM . PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD ) ;
402422 if ( ! this . user . own ( this . tdoc ) && ! this . user . hasPerm ( PERM . PERM_EDIT_CONTEST ) ) {
403423 this . applyProjection = true ;
@@ -408,12 +428,12 @@ export class RecordDetailConnectionHandler extends ConnectionHandler {
408428 problem . getStatus ( domainId , rdoc . pid , this . user . _id ) ,
409429 ] ) ;
410430
411- this . canViewCode = rdoc . uid === this . user . _id ;
431+ this . canViewCode = await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ;
412432 this . canViewCode ||= this . user . hasPriv ( PRIV . PRIV_READ_RECORD_CODE ) ;
413433 this . canViewCode ||= this . user . hasPerm ( PERM . PERM_READ_RECORD_CODE ) ;
414434 this . canViewCode ||= this . user . hasPerm ( PERM . PERM_READ_RECORD_CODE_ACCEPT ) && self ?. status === STATUS . STATUS_ACCEPTED ;
415435
416- if ( ! rdoc . contest || this . user . _id !== rdoc . uid ) {
436+ if ( ! rdoc . contest || ! ( await isOwnOrTeammateRecord ( domainId , rdoc , this . user . _id ) ) ) {
417437 if ( ! problem . canViewBy ( pdoc , this . user ) ) throw new PermissionError ( PERM . PERM_VIEW_PROBLEM_HIDDEN ) ;
418438 }
419439
0 commit comments