Skip to content

Using LCS

Lucent Fong edited this page Aug 22, 2021 · 7 revisions

How to Use the LCS API

The LCS API presents itself, initially, as a user authentication system. So first and foremost, it is that. This means you can:

  • Create users
  • Log in users for a session.
  • Have sessions carry on over time (there's a session token system)
  • Update users
  • Read user information and aggregate it.

In addition to the above features, there are a few extra things:

  • An emailing system
    • This is not essential but is really handy to support users forgetting their passwords. This deployment includes it, so it'll be documented. See the deployment stuff for more.
  • QR codes.
  • Announcement endpoints.

This has a few general ideas, not very many details. This discusses various aspects:

  • API Overview with a TL;DR on the API endpoints.
  • User Flow with the endpoints, MLH details, and logging out.
  • User Privileges outlining the types of users and what they can do.
  • The User Object explains the fields to be expected in a user. Also notes who can alter and read it. There is some detail on more advanced updates (forgotten passwords and promotions) too.
  • The registration FSM with details on the meaning of each state.
  • Relevant Mongo docs outlining the queries that can be made.

API Overview

Endpoint URL Use Docs link
/attend-event check a hacker into an event Here
/authorize Log in a current user - gets a session token Here
/create Create a new user Here
/validate Validate a session token Here
/update Update a user Here
/consume Consume a magic link. Lets users update passwords or promote users Here
/createmagiclink Lets a user make a magic link Here
/dayof-events Read events at the hackathon Here
/dayof-slack Read slack messages at the hackathon Here
/link-qr link a qr to a hacker's account Here
/send-emails Lets users (mostly administrators) send emails Here
/read Can read any user information - depending on privilege Here
/resume get resume upload/download link and see if resume is already uploaded Here
/slack-dm create a slack dm link between self and another LCS user Here
/waiver get waiver upload/download link and see if waiver is already uploaded Here

General User Flow

In general, users will go through the following flow:

  • Log in
  • Read information
  • Respond
  • Leave

Depending on when they left, they may have a valid session token and not need to login.

Hence, in terms of endpoints, the API would be invoked as:

  1. /authorize (or /validate if you're checking a token)
    • Now you save the session token if you got a new one.
  2. /read
  3. /update (may be)

Note that leaving does not require an API call. The token itself will expire in 48 hours - then the user will be "logged out" unless a new session was started. Multiple sessions are OK.

MLH Log in

MLH log in is an external source, so we do not have control over certain details. If you want to let people log in through MLH through LCS you need the following HTML:

<a href="https://my.mlh.io/oauth/authorize?client_id=<your client ID here>&redirect_uri=https://<your AWS API URL>/mlhcallback&response_type=code&scope=email+education+birthday"></a>

See the deployment instructions for how to get the client id.

See the MLH callback docs for more - you'll need to handle certain details with redirecting as LCS will re-direct back to you and needs to know how to.

A log in will handle the call to /mlhcallback for you and you'll be returned to with a session token in the URL. This is susceptible, we know, so please read the docs for more about this.

User Privileges

In running the hackathon, we find that users shouldn't be all-powerful: if there is a filtering process, wait-listing, or any case where a user many not be accepted into the hackathon, it would be useful to have different levels of users, so that organizers can use the system to maintain it.

Furthermore, it is nice to have a public API: sponsors have been known to ask for such things (non-identifiable information).

Hence, there are 3 LCS users:

  • Public users: who can read parts of the API and don't have an account.
  • Hackers: who have an account, but cannot make certain decisions.
  • Organizers: who can do anything (from an account, mostly) since they probably have the code-monkey that deployed their LCS among them.

The user object, however, tries to be more general and has the following to role field denote privilege:

{
    "hacker": True,
    "volunteer": False,
    "judge": False,
    "sponsor": False,
    "mentor": False,
    "organizer": False,
    "director": False
}

The three categories are split by the following predicates (for endpoints where this information exists):

  • Public: If there is no session token with the request.
  • Hacker: If there is a session token with the request and role.director == role.organizer == False.
  • Organizer: If there is a session token with the requestion and role.director or role.Organizer == True.

The endpoints' usability varies by the user privilege levels. Create is a weird endpoint: it elevates public users to hacker users, but has no use-case for those who intend to use LCS as public people in no way affiliated to the hackathon. So, generally, the below is based on intent when the predicates above are not applicable (otherwise the predicates above are the guidelines implemented in the code).

Endpoint URL Public Use Hacker Use Organizer Use
/attend-event No use No use checking a hacker into an event
/authorize No use Every use - logging in Every use - logging in
/create No use Every use - logging in the first time No use (there's no way to automatically become an admin)
/validate No use Every use - checking a token Every use - checking a token
/update No use Limited use - can update certain own information, here is a field-by-field guide Every use - arbitrary personal and DB updates
/consume No use Every use - can use it to change passwords or gain promotions Every use - password changes or promotions (technically promotions are not applicable in LCS, but may be to third party apps)
/createmagiclink No use Limited use - can create links for forgotten passwords only if they have a non-MLH account Can create any creatable link (no password recovery for MLH users - that's an MLH problem)
/dayof-events Every use - read events at the hackathon Every use - read events at the hackathon Every use - read events at the hackathon
/dayof-slack Every use - read slack messages at the hackathon Every use - read slack messages at the hackathon Every use - read slack messages at the hackathon
/link-qr No use No Use Every use - link a qr to someone
/send-emails No use Can send 1 email to themselves Every use - send emails to a mailing list (either an explicit list or an arbitrary query) with a list of links that can be provided per recipient.
/read Limited use - can aggregate or read non-identifiable information, here is a field-by-field guide. Limited use - can read any personal information and any non-identifiable information Every use - can read or aggregate anything
/slack-dm No use Every use - can create a slack dm link between self and any other LCS user Every use - can create a slack dm link between self and any other LCS user

The User Object

"Publicly readable" means that anybody can read it and "self-update allowed" means that the user can update it.

Field Type What? Publicly readable Self-update Allowed
email string (in HTML, <input type="email"> The user's email, a UID in the DB. No No
role object (more like HashMap<String, Boolean> if you speak Java) The user's role (details above). Yes No (see below)
votes int The "votes" that a hacker should be let in to HackRU. Yes No
password string (always hashed!) The brcypt hash of the password No No (see below)
github string The user's github handle (optional) Yes Yes
major string The user's major (optional) Yes Yes
short_answer string The user's short answer - used for user selection in HackRU Yes Yes
shirt_size string The user's shirt size (optional) Yes Yes
first_name string The user's first name (optional) No Yes
last_name string The user's last name (optional) No Yes
hackathon_count int the amount of hackthons a user has attended yes yes
qrcode string[] qrcodes linked to this user No No
dietary_restrictions string The user's dietary restrictions (optional) Yes Yes
travelling_from string A sub-object that explains the user travel data (optional) Yes Yes
special_needs string The user's special needs (optional) Yes Yes
date_of_birth string (formatted YYYY-MM-DD, courtesy of MLH) The user's date of birth (optional) Yes Yes
school string The user's school (optional) Yes Yes
grad_year string The user's graduation year (optional) Yes Yes
gender string The user's gender (optional) Yes Yes
registration_status string (really an enum) The user's registration status with it's own FSM Yes Sometimes
level_of_study string The user's level of study (optional) Yes Yes
mlh boolean Whether the user used mlh Yes No
day_of object (like a HashMap<String, Integer>) The day-of details of the user, more details below Yes No
slack_id string The user's unique slack_id No Yes

This is the default object:

    doc = { #event is the JSON passed in with all the parameters.
        "email": u_email, # this field must be here.
        "role": { #we enforce that the user is a hacker.
            "hacker": True,
            "volunteer": False,
            "judge": False,
            "sponsor": False,
            "mentor": False,
            "organizer": False,
            "director": False
        },
        "votes": 0,
        "password": password, #this is mandatory and will always be hashed.
        "github": event.get("github", ''),
        "major": event.get("major", ''),
        "short_answer": event.get("short_answer", ''),
        "shirt_size": event.get("shirt_size", ''),
        "first_name": event.get("first_name", ''),
        "last_name": event.get("last_name", ''),
        "dietary_restrictions": event.get("dietary_restrictions", ''),
        "special_needs": event.get("special_needs", ''),
        "date_of_birth": event.get("date_of_birth", ''),
        "school": event.get("school", "Rutgers University"),
        "grad_year": event.get("grad_year", ''),
        "gender": event.get("gender", ''),
        "registration_status": event.get("registration_status", "waitlist" if not is_not_day_of else "unregistered"),
        "level_of_study": event.get("level_of_study", ""),
        "mlh": mlh, #we know this, depending on how the function is called.
        "day_of":{
            "checkIn": False
        }
    }

The travel information

An example data looks somewhat like this:

{
    "is_real": true,
    "formatted_addr": "New York, NY", //the address, validated by Google
    "location": { //might be useful for some APIs
        "lat": 42.31415, //the latitude
        "lng": -78.2718 //the longitude
    },
    "mode": "car" //or "train" or "bus"
}

The day-of object

This object is unregulated by LCS to favor some flexibility. This may change so that case conventions and integer values are enforced, but a full list of events may not be provided to favor some flexibility on the backend.

Changing Passwords and Promotions

The workaround is a magic link... A link that lets users magically update things that they wouldn't be allowed to by default.

These are two similar procedures and all that changes is who creates the "magic link". We call using a magic link "consumption."

User-created links: passwords

Users create links for only themselves. They are emailed their link. This is a call to /createmagiclink.

Organizer-created links: promotions

If a user is to change role, an organizer (a user with organizer privilege) creates the link. As a convenience, they may make many links. Each link has a recipient and that recipient can consume the link. This is a call to /createmagiclink.

Link consumption

The application using LCS must call /consume to consume a magic link. The consume endpoint assumes the existence of a user. Hence, to really handle every case of a promotion, a separate call to create may be required.

The Registration FSM

I hope you see a graph...

Note: the underscores about are actually dashes in the backend. The naming below is accurate. The dashed edges labelled "Admin only" are updates that only an organizer user can make.

An explanation of the states:

  • Unregistered: The user hasn't agreed to our terms and conditions (the MLH ones) and the MLH code of conduct. We cannot accept them.
  • Registered: The use has accepted the MLH legal stuff and is awaiting admin review.
  • Rejected: The admins have rejected the user.
  • Confirmation: The user will be called upon to confirm attendance on a first-come first-serve basis.
  • Waitlist: the user is accepted but after capacity has been reached.
  • Coming: the user says they are coming (having been called upon to RSVP).
  • not-coming: the user says they are not coming (having been called upon to RSVP).
  • confirmed: the user confirmed that they're coming and their wave elapsed, so we save them a spot.
  • checked-in: the user showed up on the day-of.

Any registered user can check in through an admin so the states are mostly formalities (shh... don't tell the hackers!), but we guarantee hackers who are "confirmed" a spot at HackRU.

The Arbitrariness of Queries

When a query is allowed to be arbitrary, it means subtly different things. An arbitrary read is a mongo aggregate and the update allows for any mongo update.