Skip to content

Commit

Permalink
fix: usos-auth (#86)
Browse files Browse the repository at this point in the history
* feat: update models and validators, new endpoints, schedule groups controller

* feat: usos auth

* fix: deploy
  • Loading branch information
olekszczepanowski authored Nov 20, 2024
1 parent 2731b7a commit edb6657
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 27 deletions.
2 changes: 2 additions & 0 deletions backend/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ DB_PORT=5432
DB_USER=postgres
DB_DATABASE=postgres
DB_PASSWORD=postgres
USOS_CONSUMER_KEY=key
USOS_CONSUMER_SECRET=secret
35 changes: 18 additions & 17 deletions backend/app/controllers/session_controller.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
import { createClient } from '../usos/usos_client.js'

export default class SessionController {
async store({ request, auth, response }: HttpContext) {
async store({ request, response }: HttpContext) {
/**
* Step 1: Get credentials from the request body
*/
const { id, password } = request.only(['id', 'password'])
const { accessToken, accessSecret } = request.only(['accessToken', 'accessSecret'])
try {
const usosClient = createClient({
token: accessToken,
secret: accessSecret,
})
const profile: any = await usosClient.get(
'users/user?fields=id|student_number|first_name|last_name',
{
shouldCache: false, // Nie używamy cache dla tego zapytania
}
)
return { message: 'User logged in.', firstName: profile.first_name }
} catch (error) {
return response.unauthorized({ accessToken, accessSecret })
}

/**
* Step 2: Verify credentials
*/
const user = await User.verifyCredentials(id, password)

/**
* Step 3: Login user
*/
await auth.use('web').login(user)

/**
* Step 4: Send them to a protected route
*/
console.log(response)
}
}
103 changes: 103 additions & 0 deletions backend/app/usos/usos_client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import crypto from 'node:crypto'
import OAuth from 'oauth-1.0a'
import { LRUCache } from 'lru-cache'
import env from '#start/env'
import fetch from 'node-fetch'
const baseUrl = `https://apps.usos.pwr.edu.pl/services`

const cache = new LRUCache<string, object>({
ttl: 60 * 60 * 1000,
ttlAutopurge: false,
max: 10000,
})
const oauth = new OAuth({
consumer: { key: env.get('USOS_CONSUMER_KEY'), secret: env.get('USOS_CONSUMER_SECRET') },
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64')
},
})
export const createClient = ({ token, secret }: { token?: string; secret?: string }) => {
if (typeof token !== 'string' || typeof secret !== 'string') {
throw new Error('No token or secret provided')
}
return {
async get<R = unknown>(
endpoint: string,
{ shouldCache }: { shouldCache: boolean } = { shouldCache: true }
): Promise<R> {
const url = `${baseUrl}/${endpoint}`

if (cache.has(url) && shouldCache) {
return cache.get(url) as R
}

const data = oauth.authorize(
{
url,
method: 'GET',
},
{
key: token,
secret,
}
)
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: oauth.toHeader(data).Authorization,
},
})

if (response.status === 401) {
throw new Error('Unauthorized')
}

if (!response.ok) {
// eslint-disable-next-line no-console
console.log('Not ok', await response.text())
throw new Error('Unauthorized')
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const json: any = await response.json()

if (shouldCache) {
cache.set(url, json)
}

return json as Promise<R>
},
async post<R = unknown>(endpoint: string, body: unknown): Promise<R> {
const url = `${baseUrl}/${endpoint}`

const data = oauth.authorize(
{
url,
method: 'POST',
data: body,
includeBodyHash: true,
},
{
key: token,
secret,
}
)
const response = await fetch(url, {
method: 'POST',
headers: {
Authorization: oauth.toHeader(data).Authorization,
},
body: JSON.stringify(body),
})

if (!response.ok) {
throw new Error(response.statusText)
}

return response.json() as Promise<R>
},
}
}

export type UsosClient = ReturnType<typeof createClient>
21 changes: 21 additions & 0 deletions backend/database/migrations/1732121670545_create_users_table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BaseSchema } from '@adonisjs/lucid/schema'

export default class extends BaseSchema {
protected tableName = 'users'

async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').notNullable()
table.string('full_name').nullable()
table.string('email', 254).notNullable().unique()
table.string('password').notNullable()

table.timestamp('created_at').notNullable()
table.timestamp('updated_at').nullable()
})
}

async down() {
this.schema.dropTable(this.tableName)
}
}
Loading

0 comments on commit edb6657

Please sign in to comment.