@@ -4,26 +4,19 @@ import { testConfig } from './testConfig';
44import { testServerRunner } from './testServerRunner' ;
55import { appFactory , type TestHooks } from '../app' ;
66
7- function getUserToken ( { userAuthService } : TestHooks , userId : string ) : string {
8- return userAuthService . grantToken ( {
9- aud : 'user' ,
10- iss : 'test' ,
11- sub : userId ,
12- } ) ;
13- }
14-
157describe ( 'API retros' , ( ) => {
168 const PROPS = testServerRunner ( async ( ) => {
179 const app = await appFactory ( new TestLogger ( ) , testConfig ( ) ) ;
1810
1911 const hooks = app . testHooks ;
2012
21- await hooks . retroService . createRetro (
13+ const myRetroId = await hooks . retroService . createRetro (
2214 'nobody' ,
2315 'my-retro' ,
2416 'My Retro' ,
2517 'mood' ,
2618 ) ;
19+ await hooks . retroAuthService . setPassword ( myRetroId , 'password1' ) ;
2720
2821 await hooks . retroService . createRetro (
2922 'me' ,
@@ -151,6 +144,118 @@ describe('API retros', () => {
151144 . expect ( 401 ) ;
152145 } ) ;
153146 } ) ;
147+
148+ describe ( 'PUT /api/retros/retro-id/password' , ( ) => {
149+ it ( 'changes the retro password' , async ( props ) => {
150+ const { server, hooks } = props . getTyped ( PROPS ) ;
151+
152+ const id = ( await hooks . retroService . getRetroIdForSlug ( 'my-retro' ) ) ! ;
153+ const retroToken = await hooks . retroAuthService . grantForPassword (
154+ id ,
155+ 'password1' ,
156+ ) ;
157+ if ( ! retroToken ) {
158+ throw new Error ( 'failed to get retro token' ) ;
159+ }
160+
161+ await request ( server )
162+ . put ( `/api/retros/${ id } /password` )
163+ . send ( { password : 'password2' , evictUsers : false } )
164+ . set ( 'Authorization' , `Bearer ${ retroToken } ` )
165+ . expect ( 200 )
166+ . expect ( 'Content-Type' , / a p p l i c a t i o n \/ j s o n / ) ;
167+
168+ // existing token is still valid
169+ expect (
170+ await hooks . retroAuthService . readAndVerifyToken ( id , retroToken ) ,
171+ ) . isTruthy ( ) ;
172+
173+ // new token requests must use new password
174+ expect (
175+ await hooks . retroAuthService . grantForPassword ( id , 'password1' ) ,
176+ ) . isNull ( ) ;
177+
178+ const retroToken2 = await hooks . retroAuthService . grantForPassword (
179+ id ,
180+ 'password2' ,
181+ ) ;
182+ expect ( retroToken2 ) . isTruthy ( ) ;
183+ expect (
184+ await hooks . retroAuthService . readAndVerifyToken ( id , retroToken2 ! ) ,
185+ ) . isTruthy ( ) ;
186+ } ) ;
187+
188+ it ( 'voids existing tokens if requested' , async ( props ) => {
189+ const { server, hooks } = props . getTyped ( PROPS ) ;
190+
191+ const id = ( await hooks . retroService . getRetroIdForSlug ( 'my-retro' ) ) ! ;
192+ const retroToken = await hooks . retroAuthService . grantForPassword (
193+ id ,
194+ 'password1' ,
195+ ) ;
196+ if ( ! retroToken ) {
197+ throw new Error ( 'failed to get retro token' ) ;
198+ }
199+
200+ await request ( server )
201+ . put ( `/api/retros/${ id } /password` )
202+ . send ( { password : 'password2' , evictUsers : true } )
203+ . set ( 'Authorization' , `Bearer ${ retroToken } ` )
204+ . expect ( 200 )
205+ . expect ( 'Content-Type' , / a p p l i c a t i o n \/ j s o n / ) ;
206+
207+ // existing token is no longer valid
208+ expect (
209+ await hooks . retroAuthService . readAndVerifyToken ( id , retroToken ) ,
210+ ) . isNull ( ) ;
211+
212+ // new tokens are valid
213+ const retroToken2 = await hooks . retroAuthService . grantForPassword (
214+ id ,
215+ 'password2' ,
216+ ) ;
217+ expect ( retroToken2 ) . isTruthy ( ) ;
218+ expect (
219+ await hooks . retroAuthService . readAndVerifyToken ( id , retroToken2 ! ) ,
220+ ) . isTruthy ( ) ;
221+ } ) ;
222+
223+ it ( 'responds HTTP Unauthorized if no credentials are given' , async ( props ) => {
224+ const { server, hooks } = props . getTyped ( PROPS ) ;
225+
226+ const id = ( await hooks . retroService . getRetroIdForSlug ( 'my-retro' ) ) ! ;
227+ await request ( server )
228+ . put ( `/api/retros/${ id } /password` )
229+ . send ( { password : 'password2' , evictUsers : false } )
230+ . expect ( 401 ) ;
231+ } ) ;
232+
233+ it ( 'responds HTTP Unauthorized if credentials are incorrect' , async ( props ) => {
234+ const { server, hooks } = props . getTyped ( PROPS ) ;
235+
236+ const id = ( await hooks . retroService . getRetroIdForSlug ( 'my-retro' ) ) ! ;
237+ await request ( server )
238+ . put ( `/api/retros/${ id } /password` )
239+ . send ( { password : 'password2' , evictUsers : false } )
240+ . set ( 'Authorization' , 'Bearer Foo' )
241+ . expect ( 401 ) ;
242+ } ) ;
243+
244+ it ( 'responds HTTP Forbidden if scope is not "manage"' , async ( props ) => {
245+ const { server, hooks } = props . getTyped ( PROPS ) ;
246+
247+ const id = ( await hooks . retroService . getRetroIdForSlug ( 'my-retro' ) ) ! ;
248+ const retroToken = await getRetroToken ( hooks , id , {
249+ manage : false ,
250+ } ) ;
251+
252+ await request ( server )
253+ . put ( `/api/retros/${ id } /password` )
254+ . send ( { password : 'password2' , evictUsers : false } )
255+ . set ( 'Authorization' , `Bearer ${ retroToken } ` )
256+ . expect ( 403 ) ;
257+ } ) ;
258+ } ) ;
154259} ) ;
155260
156261describe ( 'API retros with my retros disabled' , ( ) => {
@@ -201,3 +306,25 @@ describe('API retros with my retros disabled', () => {
201306 } ) ;
202307 } ) ;
203308} ) ;
309+
310+ function getUserToken ( { userAuthService } : TestHooks , userId : string ) : string {
311+ return userAuthService . grantToken ( {
312+ aud : 'user' ,
313+ iss : 'test' ,
314+ sub : userId ,
315+ } ) ;
316+ }
317+
318+ function getRetroToken (
319+ { retroAuthService } : TestHooks ,
320+ retroId : string ,
321+ scopes = { } ,
322+ ) : Promise < string | null > {
323+ return retroAuthService . grantToken ( retroId , {
324+ read : true ,
325+ readArchives : true ,
326+ write : true ,
327+ manage : true ,
328+ ...scopes ,
329+ } ) ;
330+ }
0 commit comments