Skip to content

Commit

Permalink
Implement middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
ijo42 committed Jul 31, 2024
1 parent 11b04c3 commit aff7801
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 123 deletions.
31 changes: 31 additions & 0 deletions .idx/dev.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
# Which nixpkgs channel to use.
channel = "stable-23.11"; # or "unstable"
# Use https://search.nixos.org/packages to find packages
packages = [
pkgs.go
pkgs.air
];
# Sets environment variables in the workspace
env = {};
idx = {
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
extensions = [
"golang.go"
"rangav.vscode-thunder-client"
];
workspace = {
onCreate = {
# Open editors for the following files by default, if they exist:
default.openFiles = ["main.go"];
};
# Runs when a workspace is (re)started
onStart= {
run-server = "air";
};
# To run something each time the workspace is first created, use the `onStart` hook
};
};
}
11 changes: 5 additions & 6 deletions .traefik.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
displayName: Demo Plugin
displayName: ICal middleware
type: middleware
iconPath: .assets/icon.png

import: github.com/traefik/plugindemo
import: github.com/psumaps/icalmiddleware

summary: '[Demo] Add Request Header'
summary: '[Auth] Authorize though ical endpoint'

testData:
Headers:
X-Demo: test
X-URL: '{{URL}}'
HeaderName: Authorization

66 changes: 0 additions & 66 deletions demo.go

This file was deleted.

49 changes: 0 additions & 49 deletions demo_test.go

This file was deleted.

4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/traefik/plugindemo
module github.com/psumaps/icalmiddleware

go 1.19
go 1.21
127 changes: 127 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package icalmiddleware

import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
)

type Config struct {
ForwardToken bool `json:"forwardToken,omitempty"`
Freshness int64 `json:"freshness,omitempty"`
HeaderName string `json:"headerName,omitempty"`
}

func CreateConfig() *Config {
return &Config{
HeaderName: "Authorization",
ForwardToken: false,
Freshness: 3600,
}
}

type ICalMiddleware struct {
next http.Handler
headerName string
forwardToken bool
freshness int64
cache map[string]time.Time
name string
}

func New(_ context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
return &ICalMiddleware{
headerName: config.HeaderName,
forwardToken: config.ForwardToken,
freshness: config.Freshness,
next: next,
cache: make(map[string]time.Time),
name: name,
}, nil
}

func (plugin *ICalMiddleware) getCached(key string) bool {
item, found := plugin.cache[key]
if found && time.Now().Before(item) {
return true
}
return false
}

func (plugin *ICalMiddleware) setCache(key string) {
plugin.cache[key] = time.Now().Add(time.Duration(plugin.freshness) * time.Second)
}

func (plugin *ICalMiddleware) httpRequestAndCache(url string) error {
response, err := http.Get("https://ical.psu.ru/calendars/" + url)
if err != nil {
return fmt.Errorf("request error: %v", err)
}
defer response.Body.Close()

body := make([]byte, 20)

_, err = io.ReadAtLeast(response.Body, body, 20)
if err != nil {
return fmt.Errorf("read error: %v", err)
}

result := string(body)

if strings.HasPrefix(result, "BEGIN") {
plugin.setCache(url)
fmt.Println("Request valid")
} else {
fmt.Println("Request invalid")
return fmt.Errorf("request invalid")
}

return nil
}

// extractTokenFromHeader extracts the token from the header. If the token is found, it is removed from the header unless forwardToken is true.
func (plugin *ICalMiddleware) extractTokenFromHeader(request *http.Request) string {
header, ok := request.Header[plugin.headerName]
if !ok {
return ""
}

token := header[0]

if !plugin.forwardToken {
request.Header.Del(plugin.headerName)
}

if strings.HasPrefix(token, "Bearer ") {
return token[7:]
}
return token
}

// validate validates the request and returns the HTTP status code or an error if the request is not valid. It also sets any headers that should be forwarded to the backend.
func (plugin *ICalMiddleware) validate(request *http.Request) (int, error) {
token := plugin.extractTokenFromHeader(request)
if token == "" {
// No token provided
return http.StatusUnauthorized, fmt.Errorf("no token provided")
} else if !plugin.getCached(token) {
// Token provided
err := plugin.httpRequestAndCache(token)
if err != nil {
return http.StatusUnauthorized, err
}
}
return http.StatusOK, nil
}

func (a *ICalMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
_, err := a.validate(req)
if err != nil {
http.Error(rw, "Unauthorized. Attach valid ICal ETIS token in "+a.headerName+" header", http.StatusUnauthorized)
return
}
a.next.ServeHTTP(rw, req)
}

0 comments on commit aff7801

Please sign in to comment.