Skip to content

Commit

Permalink
feat: implement bouncer
Browse files Browse the repository at this point in the history
This service authorizes calls and converts extensions to users. This
information can be used in Asterisk to take action.
  • Loading branch information
crazybolillo committed Jul 31, 2024
1 parent 1227ce7 commit 3265600
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 0 deletions.
5 changes: 5 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"github.com/crazybolillo/eryth/internal/bouncer"
"github.com/crazybolillo/eryth/internal/handler"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
Expand Down Expand Up @@ -75,6 +76,10 @@ func serve(ctx context.Context) error {
endpoint := handler.Endpoint{Conn: conn}
r.Mount("/endpoint", endpoint.Router())

checker := &bouncer.Bouncer{Conn: conn}
authorization := handler.Authorization{Bouncer: checker}
r.Mount("/bouncer", authorization.Router())

slog.Info("Listening on :8080")
return http.ListenAndServe(":8080", r)
}
39 changes: 39 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
definitions:
bouncer.Response:
properties:
allow:
type: boolean
destination:
type: string
type: object
handler.AuthorizationRequest:
properties:
endpoint:
type: string
extension:
type: string
type: object
handler.createEndpointRequest:
properties:
codecs:
Expand Down Expand Up @@ -36,6 +50,31 @@ info:
title: Asterisk Administration API
version: "1.0"
paths:
/bouncer:
post:
consumes:
- application/json
parameters:
- description: Action to be reviewed
in: body
name: payload
required: true
schema:
$ref: '#/definitions/handler.AuthorizationRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/bouncer.Response'
"400":
description: Bad Request
"500":
description: Internal Server Error
summary: Determine whether the specified action (call) is allowed or not.
tags:
- bouncer
/endpoint:
post:
consumes:
Expand Down
43 changes: 43 additions & 0 deletions internal/bouncer/bouncer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package bouncer

import (
"context"
"github.com/crazybolillo/eryth/internal/db"
"github.com/crazybolillo/eryth/internal/sqlc"
"github.com/jackc/pgx/v5"
"log/slog"
)

type Response struct {
Allow bool `json:"allow"`
Destination string `json:"destination"`
}

type Bouncer struct {
*pgx.Conn
}

func (b *Bouncer) Check(ctx context.Context, endpoint, dialed string) Response {

Check failure on line 20 in internal/bouncer/bouncer.go

View workflow job for this annotation

GitHub Actions / lint

SA4009: argument endpoint is overwritten before first use (staticcheck)
result := Response{
Allow: false,
Destination: "",
}

tx, err := b.Begin(ctx)
if err != nil {
slog.Error("Unable to start transaction", slog.String("reason", err.Error()))
return result
}

queries := sqlc.New(tx)
endpoint, err = queries.GetEndpointByExtension(ctx, db.Text(dialed))

Check failure on line 33 in internal/bouncer/bouncer.go

View workflow job for this annotation

GitHub Actions / lint

SA4009(related information): assignment to endpoint (staticcheck)
if err != nil {
slog.Error("Failed to retrieve endpoint", slog.String("dialed", dialed), slog.String("reason", err.Error()))
return result
}

return Response{
Allow: true,
Destination: endpoint,
}
}
63 changes: 63 additions & 0 deletions internal/handler/authorization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package handler

import (
"context"
"encoding/json"
"github.com/crazybolillo/eryth/internal/bouncer"
"github.com/go-chi/chi/v5"
"log/slog"
"net/http"
)

type CallBouncer interface {
Check(ctx context.Context, endpoint, dialed string) bouncer.Response
}

type Authorization struct {
Bouncer CallBouncer
}

type AuthorizationRequest struct {
From string `json:"endpoint"`
Extension string `json:"extension"`
}

func (e *Authorization) Router() chi.Router {
r := chi.NewRouter()
r.Post("/", e.post)

return r
}

// @Summary Determine whether the specified action (call) is allowed or not.
// @Accept json
// @Produce json
// @Param payload body AuthorizationRequest true "Action to be reviewed"
// @Success 200 {object} bouncer.Response
// @Failure 400
// @Failure 500
// @Tags bouncer
// @Router /bouncer [post]
func (e *Authorization) post(w http.ResponseWriter, r *http.Request) {
var payload AuthorizationRequest
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&payload)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

response := e.Bouncer.Check(r.Context(), payload.From, payload.Extension)
content, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err = w.Write(content)
if err != nil {
slog.Error("Failed to write response", slog.String("path", r.URL.Path), slog.String("reason", err.Error()))
}
}
18 changes: 18 additions & 0 deletions internal/sqlc/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,13 @@ INSERT INTO ery_extension
(endpoint_id, extension)
VALUES
($1, $2);

-- name: GetEndpointByExtension :one
SELECT
ps_endpoints.id
FROM
ps_endpoints
INNER JOIN
ery_extension ee on ps_endpoints.sid = ee.endpoint_id
WHERE
ee.extension = $1;

0 comments on commit 3265600

Please sign in to comment.