GithubHelp home page GithubHelp logo

testaustime-backend's Introduction

testaustime-backend's People

Contributors

bluelhf avatar chicken avatar drvilepis avatar eldemarkki avatar faitti avatar ihmemaassa avatar j3rq avatar lajp avatar merkush1 avatar raikasdev avatar romeq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

testaustime-backend's Issues

`/leaderboards/{name}/regenerate` innecessarily requires an empty body

If I do curl --request POST "https://api.testaustime.fi/leaderboards/[redacted]/regenerate" --header "Authorization: Bearer [redacted]" it will return 400 Bad Request, even though the API spec says that that is okay.

I must instead do curl --request POST "https://api.testaustime.fi/leaderboards/[redacted]/regenerate" --header "Authorization: Bearer [redacted]" -H "Content-Type: application/json" --data-raw "" to get it to regenerate the token.

We should ideally not require any body, or at least update the API spec to include this requirement.

`/auth/changepassword` doesn't require secure access token

The API specification says that the /auth/changepassword endpoint requires a secured access token but in reality it doesn't. We should either remove the requirement from the API documentation, or change the logic to require it.

I'd recommend just removing it from the API docs and keeping the implementation as is, because the endpoint requires the same values as the secure access token generation endpoint, so requiring SAT for changing password doesn't really provide any benefits.

Provide an OpenAPI schema

It would be nice to have an OpenAPI schema for the API, it would allow us to generate the types for the frontend automatically.

One possible library: https://github.com/juhaku/utoipa

Not sure what is the best way to distribute the schema, here are some ideas:

  • Serve it from an endpoint
  • Store it in the repository
    • Add checks in CI to ensure the schema is up-to-date
    • I would prefer this option so that I won't need to run the backend locally every time I want to get the latest schema

Add endpoint for getting leaderboard metadata by invite code

We need an endpoint for getting leaderboard metadata by an invite code for Testaustime/testaustime-frontend#270

The metadata should include at least:

  • Leaderboard name
  • Member count

The endpoint could for example be GET /leaderboards/invite-code/ttlic_xyz123. The endpoint should not require authentication, because the links should be shareable to anonymous users and we should show them the metadata and prompt them to register to Testaustime in order to join the leaderboard.

Add ratelimiting headers

Headers such as x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset would be useful for client libraries to queue http request and not get ratelimited.

Create route for easier access to aggregated leaderboard data

There should be a route that returns the list of all the leaderboards the user is a member of, AND it should include basic info about the leaderboard, such as the top coder and the user's rank on it.

This is currently a problem because /users/@me/leaderboards only returns a list of leaderboard names. The client will then have to do another request for each of those leaderboards to /leaderboards/{name}, leading to an n+1 problem.

The new route would simplify client code and also prevent users from hitting rate limits if they are a member of many leaderboards.

API Documentation

Update the API documentation.
If feeling extra fancy, create a new tool which auto generates the documentation from comments in the code and just write a doc comment above the endpoint in the code when implementing new features.

The `/friends/list` endpoint should return coding time data

Currently it only returns a list of usernames: ["friend1", "friend2"]
It should return data about how much the user has coded, because the leaderboard and friend list need that data.

The new format could be this, for example:

[
  {
    "username": "friend1",
    "timeCoded": {
      "allTime": 77533,
      "lastMonth": 53290,
      "lastWeek": 1863
    }
  },
  {
    "username": "friend2",
    "timeCoded": {
      "allTime": 181932,
      "lastMonth": 83912,
      "lastWeek": 27184
    }
  }
]

Routes that don't require authentication should ignore the `Authorization` header

Currently if I send the following request

curl 'https://api.testaustime.fi/auth/login' -X POST -H 'Authorization: Bearer' -H 'Content-Type: application/json' --data-raw '{"username":"Eldemarkki","password":"[REDACTED]"}'

I get a response You are not authorized, because it tries to find a user with that Authorization header.

I should instead get a login success/error response.

Problems with the last admin of a leaderboard deleting their account

If the last admin of a leaderboard deletes their account, the leaderboard will be left dangling in the database. Same error as #24

I can think of 2 possible solutions:

  • After an admin deletes their account, it also deletes the leaderboard (but shows a notification “This will also delete leaderboards X, Y and Z. Are you sure?”)
  • Don't allow the user to delete their account before they have promoted another admin or deleted the leaderboard
    • This is worse in my opinion, because users should be able to delete their data as easily as possible for data privacy/safety reasons

Create seed data for database

There should be an easy way to initialize the database with some seed data. This would make it easier for new people to start contributing to this project.

Documentation incorrect for `GET /users/{username}/activity/current`

The documentation specifies that the GET request to /users/{username}/activity/current always specifies that the started field is in UTC time (by including the Z at the end):

{
    "started": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
    "duration": "10",
    "heartbeat": {
        "language": "c",
        "hostname": "hostname1",
        "editor_name": "Neovim",
        "project_name": "cool_project22"
    }
}

However, in reality that "Z" is not included. Here is an example response that I got:

{
  "started": "2023-12-17T12:03:20.938417739",
  "duration": 240,
  "heartbeat": {
    "project_name": "testaustime-frontend",
    "language": "typescript",
    "editor_name": "vscode",
    "hostname": "ideapad"
  }
}

I believe the same problem exists for all other timestamps that are returned from any endpoint. We already have an issue in the frontend for this, but I think it would be best to fix in the backend.

Also, it seems that the duration field is a number instead of a string.

On Testaustime client authentication and endpoint security

Status quo

Currently, a Testaustime client embedded as an add-on in a code editor (such as Neovim or Micro) is authenticated using the authentication token of the account. This is sub-optimal, to put it nicely. There is no way to distinguish between the user performing actions on the front-end (or, if they are particularly based, with direct API calls) and a Testaustime client performing the same actions.

Graph of user and client with same token

Impact

As Testaustime does not have many security-critical endpoints, this is mostly a non-issue. Moreover, the endpoint to delete the current user (/users/@me/delete) requires the username and password anyway, presumably for this very reason. That being said, endpoints related to leaderboard administration or user management (joining and leaving leaderboards, removing and adding friends) are wholly unprotected. This lack of protection is concerning, and should be changed.

Graph of client changing the user's name to something mean

Proposal

I propose a second layer of authentication reserved for administrative and management tasks. Endpoints that are not considered security-critical (in practice, all read-only endpoints as well as activity updates) would be placed on the primary layer, which would use the traditional and current form of authentication. Endpoints that are considered security-critical, i.e. ones that an attacker could use to cause significant distress to the end-user, would be guarded using the new secured layer.

Endpoints guarded by the secured layer are only accessible by special, temporary tokens. A secured layer authentication token can be generated using a primary layer endpoint (e.g. /auth/securedaccess). This endpoint would require both the username and password of the user, as well as the traditional authentication token. It would then respond with a temporary token to be used in place of the traditional authentication token, as well as metadata about the lifetime of the token.

Graph of user changing their own name to something cool using cool proposal

Secured endpoints such as leaderboard, friends list and user management would then be accessed using this temporary token instead of the traditional one. This way, endpoints of the secured layer are essentially guarded by an additional login step (like "sudo mode" in GitHub).

Closing remarks

I'd like to hear your thoughts on the issue. Crucially, I'd like feedback on whether the Testaustime developers actually think this change is necessary. The likelihood of tokens being compromised is quite low, especially as Testaustime's target audience is mostly one of programmers who are (or, at least, ought to be) familiar with token authentication, and probably wouldn't go about their day handing tokens out to anyone who asks. That being said, I still think this change is justified in the name of correctness if not that of security.

Better error if a user doesn't have a current activity

If a user doesn't have a current activity, but their current activity is requested at /users/USERNAME/activity/current, it will just return null with status 200.

$ curl 'https://api.testaustime.fi/users/Antti/activity/current' -H 'Authorization: Bearer REDACTED'
null

(sorry Antti for exposing your inactivity)

I suggest we return some JSON (e.g. {"error":"User doesn't have an active session"}) with status code 404.

Create route to get aggregated data about the whole instance

The info could be, for example

  • Total time coded (among all users)
  • Most used programming language (among all users)
  • User count

The privacy implications of this should be considered as well. Should this for example be only opt-in?

For the people who might self-host Testaustime (not sure if there are any at the moment), it should also be possible for the admins to disable these data routes via e.g. an environment variable.

Response inconsistancy

Leaderboard codes are returned with the ttlic_ prefix but friend codes are not returned with the ttfc_ prefix.

Show better error when trying to log in with a user that doesn't exist

Currently it throws a Diesel error if trying to log in with a non-existent user:
curl 'https://api.testaustime.fi/auth/login' -X POST -H 'Content-Type: application/json' --data-raw '{"username":"thisuserdoesntexist","password":"thisuserdoesntexist"}' returns {"error":"Diesel transaction failed `Record not found`"}. It should instead return a generic error, such as {"error":"Invalid credentials"}

Note that those specific credentials may start working if someone creates that user after seeing this issue. This can be bypassed by purchasing a cat and making it run over your keyboard to generate a new, hopefully non-existent user.

Partially fixes Testaustime/testaustime-frontend#63 also.

Admins can leave a leaderboard and there won't be more admins

Currently if a leaderboard only has user X in it, and X is an admin, they can leave the leaderboard. Then the leaderboard won't have any admins, and so no one can delete it and it will stay in the database forever.

I propose that an admin should only be allowed to leave a leaderboard if there are other admins left. If there aren't any, the last admin should only be able to delete the leaderboard.

Collect email address optionally for resetting password

We could allow the user to store one (or multiple?) emails in their profile, so that in case they forget their password, we can send a password-reset link to their email.

We should probably still use username-password auth, as changing that would be quite a big change.

We'd need some API to send the emails, we could ask if Testausserveri ry could pay for it. For local development, MailHog looks like a very useful tool.

Bot functionality

To ease the development of extra functionality such as the Testaustime Discord bot there could be a seperate API where such bot could access user data if they have consented to it in some settings. Would be a nicer alternative to using an user account with a private leaderboard and a ton of friends.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.