generated from traefik/plugindemo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
165 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |