99 "testing"
1010
1111 "github.com/matrix-org/policyserv/config"
12+ "github.com/matrix-org/policyserv/internal"
1213 "github.com/matrix-org/policyserv/storage"
1314 "github.com/matrix-org/policyserv/test"
1415 "github.com/stretchr/testify/assert"
@@ -75,13 +76,15 @@ func TestCreateCommunityCreate(t *testing.T) {
7576 assert .Equal (t , communityName , community .Name )
7677 assert .NotEmpty (t , community .CommunityId )
7778 assert .NotNil (t , community .Config )
79+ assert .Nil (t , community .ApiAccessToken ) // no access token should be set on creation
7880
7981 // Ensure it was also stored
8082 fromDb , err := api .storage .GetCommunity (context .Background (), community .CommunityId )
8183 assert .NoError (t , err )
8284 assert .NotNil (t , fromDb )
8385 assert .Equal (t , communityName , fromDb .Name )
8486 assert .Equal (t , community .CommunityId , fromDb .CommunityId )
87+ assert .Nil (t , fromDb .ApiAccessToken ) // no access token should be set on creation
8588
8689 // Note: we can't (currently) test that errors during database calls and HTTP responses are handled. A future test
8790 // case *should* cover this.
@@ -124,6 +127,12 @@ func TestGetCommunity(t *testing.T) {
124127 assert .NotEmpty (t , community .CommunityId )
125128 assert .Equal (t , name , community .Name )
126129
130+ // Set an access token for the community. This is to ensure we don't leak it through the request.
131+ community .ApiAccessToken = internal .Pointer ("pst_TESTING" )
132+ err = api .storage .UpsertCommunity (context .Background (), community )
133+ assert .NoError (t , err )
134+ community .ApiAccessToken = nil // so the assert.Equal() passes later
135+
127136 w := httptest .NewRecorder ()
128137 r := httptest .NewRequest (http .MethodGet , "/api/v1/communities/" + community .CommunityId , nil )
129138 r .SetPathValue ("id" , community .CommunityId )
@@ -178,6 +187,12 @@ func TestSetCommunityConfig(t *testing.T) {
178187 assert .NotEmpty (t , community .CommunityId )
179188 assert .Equal (t , name , community .Name )
180189
190+ // Set an access token for the community. This is to ensure we don't leak it through the request.
191+ community .ApiAccessToken = internal .Pointer ("pst_TESTING" )
192+ err = api .storage .UpsertCommunity (context .Background (), community )
193+ assert .NoError (t , err )
194+ community .ApiAccessToken = nil // so the assert.Equal() passes later
195+
181196 cnf := & config.CommunityConfig {
182197 KeywordFilterKeywords : & []string {"keyword1" , "keyword2" },
183198 }
@@ -196,3 +211,74 @@ func TestSetCommunityConfig(t *testing.T) {
196211 // Note: we can't (currently) test that errors during database calls and HTTP responses are handled. A future test
197212 // case *should* cover this.
198213}
214+
215+ func TestRotateCommunityAccessTokenWrongMethod (t * testing.T ) {
216+ t .Parallel ()
217+
218+ api := makeApi (t )
219+
220+ w := httptest .NewRecorder ()
221+ r := httptest .NewRequest (http .MethodGet /*this should be POST*/ , "/api/v1/communities/not_a_real_id/rotate_access_token" , nil )
222+ httpSetCommunityConfigApi (api , w , r )
223+ assert .Equal (t , http .StatusMethodNotAllowed , w .Code )
224+ test .AssertApiError (t , w , "M_UNRECOGNIZED" , "Method not allowed" )
225+ }
226+
227+ func TestRotateCommunityAccessTokenNotFound (t * testing.T ) {
228+ t .Parallel ()
229+
230+ api := makeApi (t )
231+
232+ cnf := & config.CommunityConfig {
233+ KeywordFilterKeywords : & []string {"keyword1" , "keyword2" },
234+ }
235+ w := httptest .NewRecorder ()
236+ r := httptest .NewRequest (http .MethodPost , "/api/v1/communities/not_a_real_id/rotate_access_token" , test .MakeJsonBody (t , cnf ))
237+ r .SetPathValue ("id" , "not_a_real_id" )
238+ httpSetCommunityConfigApi (api , w , r )
239+ assert .Equal (t , http .StatusNotFound , w .Code )
240+ test .AssertApiError (t , w , "M_NOT_FOUND" , "Community not found" )
241+ }
242+
243+ func TestRotateCommunityAccessToken (t * testing.T ) {
244+ t .Parallel ()
245+
246+ api := makeApi (t )
247+
248+ name := "Test Community"
249+ community , err := api .storage .CreateCommunity (context .Background (), name )
250+ assert .NoError (t , err )
251+ assert .NotNil (t , community )
252+ assert .NotEmpty (t , community .CommunityId )
253+ assert .Equal (t , name , community .Name )
254+ assert .Nil (t , community .ApiAccessToken ) // should be created without an access token
255+
256+ // First rotation should have an empty "old_access_token" and a non-empty "new_access_token"
257+ w := httptest .NewRecorder ()
258+ r := httptest .NewRequest (http .MethodPost , "/api/v1/communities/" + community .CommunityId + "/rotate_access_token" , nil )
259+ r .SetPathValue ("id" , community .CommunityId )
260+ httpRotateCommunityAccessTokenApi (api , w , r )
261+ assert .Equal (t , http .StatusOK , w .Code )
262+ fromRes := make (map [string ]string )
263+ err = json .Unmarshal (w .Body .Bytes (), & fromRes )
264+ assert .NoError (t , err )
265+ assert .Empty (t , fromRes ["old_access_token" ])
266+ assert .NotEmpty (t , fromRes ["new_access_token" ])
267+
268+ // Verify the access token was persisted by the HTTP handler
269+ accessToken := fromRes ["new_access_token" ]
270+ fromDb , err := api .storage .GetCommunity (context .Background (), community .CommunityId )
271+ assert .NoError (t , err )
272+ assert .NotNil (t , fromDb )
273+ assert .Equal (t , accessToken , internal .Dereference (fromDb .ApiAccessToken ))
274+
275+ // Second rotation should reference what was the last request's "new" access token and generate yet another new one
276+ w = httptest .NewRecorder ()
277+ httpRotateCommunityAccessTokenApi (api , w , r )
278+ assert .Equal (t , http .StatusOK , w .Code )
279+ fromRes = make (map [string ]string )
280+ err = json .Unmarshal (w .Body .Bytes (), & fromRes )
281+ assert .NoError (t , err )
282+ assert .Equal (t , accessToken , fromRes ["old_access_token" ]) // old token should match previous rotation
283+ assert .NotEmpty (t , fromRes ["new_access_token" ])
284+ }
0 commit comments