Skip to content

Commit 52a9ab7

Browse files
committed
add unauthorised200 parameter to /user endpoint
1 parent 877765c commit 52a9ab7

File tree

5 files changed

+80
-7
lines changed

5 files changed

+80
-7
lines changed

backend/app/rest/api/rest.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,17 @@ func (s *Rest) routes() chi.Router {
291291
rauth.Use(middleware.Timeout(30 * time.Second))
292292
rauth.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil)))
293293
rauth.Use(authMiddleware.Auth, matchSiteID, middleware.NoCache, logInfoWithBody)
294-
rauth.Get("/user", s.privRest.userInfoCtrl)
295294
rauth.Get("/userdata", s.privRest.userAllDataCtrl)
296295
})
297296

297+
// protected routes, user is set but checked inside the handlers
298+
rapi.Group(func(rauthOptional chi.Router) {
299+
rauthOptional.Use(middleware.Timeout(30 * time.Second))
300+
rauthOptional.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil)))
301+
rauthOptional.Use(authMiddleware.Trace, middleware.NoCache, logInfoWithBody)
302+
rauthOptional.Get("/user", s.privRest.userInfoCtrl)
303+
})
304+
298305
// admin routes, require auth and admin users only
299306
rapi.Route("/admin", func(radmin chi.Router) {
300307
radmin.Use(middleware.Timeout(30 * time.Second))

backend/app/rest/api/rest_private.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,21 @@ func (s *private) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
236236
render.JSON(w, r, res)
237237
}
238238

239-
// GET /user?site=siteID - returns user info
239+
// GET /user?site=siteID&unauthorised200=false - returns user info, with unauthorised200=true returns 200 with error message
240240
func (s *private) userInfoCtrl(w http.ResponseWriter, r *http.Request) {
241-
user := rest.MustGetUserInfo(r)
241+
user, err := rest.GetUserInfo(r)
242+
if err != nil {
243+
if r.URL.Query().Get("unauthorised200") == "true" {
244+
render.JSON(w, r, R.JSON{"error": err.Error()})
245+
return
246+
}
247+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
248+
return
249+
}
250+
251+
// as user is set, call matchSiteID middleware to verify SiteID match
252+
matchSiteID(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})).ServeHTTP(w, r)
253+
242254
if siteID := r.URL.Query().Get("site"); siteID != "" {
243255
user.Verified = s.dataService.IsVerified(siteID, user.ID)
244256

backend/app/rest/api/rest_private_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,44 @@ func TestRest_TelegramNotification(t *testing.T) {
13911391
assert.Empty(t, mockDestination.Get()[3].Telegrams)
13921392
}
13931393

1394+
func TestRest_UserUnauthorised200(t *testing.T) {
1395+
ts, _, teardown := startupT(t)
1396+
defer teardown()
1397+
1398+
client := &http.Client{Timeout: 1 * time.Second}
1399+
defer client.CloseIdleConnections()
1400+
req, err := http.NewRequest("GET", ts.URL+"/api/v1/user?site=remark42", http.NoBody)
1401+
require.NoError(t, err)
1402+
resp, err := client.Do(req)
1403+
require.NoError(t, err)
1404+
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
1405+
body, err := io.ReadAll(resp.Body)
1406+
assert.NoError(t, resp.Body.Close())
1407+
assert.NoError(t, err)
1408+
assert.Equal(t, "Unauthorized\n", string(body))
1409+
1410+
req, err = http.NewRequest("GET", ts.URL+"/api/v1/user?site=remark42&unauthorised200=true", http.NoBody)
1411+
require.NoError(t, err)
1412+
resp, err = client.Do(req)
1413+
require.NoError(t, err)
1414+
require.Equal(t, http.StatusOK, resp.StatusCode, "should fail but with status code 200 due to the unauthorised200 param set")
1415+
body, err = io.ReadAll(resp.Body)
1416+
assert.NoError(t, resp.Body.Close())
1417+
assert.NoError(t, err)
1418+
assert.Equal(t, `{"error":"can't extract user info from the token: user can't be parsed"}`+"\n", string(body))
1419+
1420+
req, err = http.NewRequest("GET", ts.URL+"/api/v1/user?site=wrong_site&unauthorised200=true", http.NoBody)
1421+
require.NoError(t, err)
1422+
req.Header.Add("X-JWT", devToken)
1423+
resp, err = client.Do(req)
1424+
require.NoError(t, err)
1425+
require.Equal(t, http.StatusForbidden, resp.StatusCode, "should fail due to site mismatch")
1426+
body, err = io.ReadAll(resp.Body)
1427+
assert.NoError(t, resp.Body.Close())
1428+
assert.NoError(t, err)
1429+
assert.Contains(t, string(body), "Access denied\n")
1430+
}
1431+
13941432
func TestRest_UserAllData(t *testing.T) {
13951433
ts, srv, teardown := startupT(t)
13961434
defer teardown()

frontend/apps/remark42/app/common/api.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,16 @@ export const removeMyComment = (id: Comment['id']): Promise<void> =>
6060

6161
export const getPreview = (text: string): Promise<string> => apiFetcher.post('/preview', {}, { text });
6262

63-
export function getUser(): Promise<User | null> {
64-
return apiFetcher.get<User | null>('/user').catch(() => null);
63+
export function getUser(unauthorised200= true): Promise<User | null> {
64+
return apiFetcher
65+
.get<User | null>('/user', { unauthorised200: String(unauthorised200) })
66+
.then((response) => {
67+
if (unauthorised200 && response && 'error' in response) {
68+
return null;
69+
}
70+
return response;
71+
})
72+
.catch(() => null);
6573
}
6674

6775
export const uploadImage = (image: File): Promise<Image> => {

frontend/packages/api/clients/public.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,16 @@ export function createPublicClient({ siteId: site, baseUrl }: ClientParams) {
9393
/**
9494
* Get current authorized user
9595
*/
96-
async function getUser(): Promise<User | null> {
97-
return fetcher.get<User | null>('/user').catch(() => null)
96+
async function getUser(unauthorised200= true): Promise<User | null> {
97+
return fetcher
98+
.get<User | null>('/user', { unauthorised200: String(unauthorised200) })
99+
.then((response) => {
100+
if (unauthorised200 && response && 'error' in response) {
101+
return null;
102+
}
103+
return response;
104+
})
105+
.catch(() => null);
98106
}
99107

100108
/**

0 commit comments

Comments
 (0)