mirror of
https://github.com/kataras/iris.git
synced 2026-01-08 04:21:57 +00:00
Embrace the weekend- Update to rc.3 | Read the HISTORY.md
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
# Middleware
|
||||
Iris has its tiny middlewares here.
|
||||
@@ -1,157 +0,0 @@
|
||||
package basicauth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type (
|
||||
encodedUser struct {
|
||||
HeaderValue string
|
||||
Username string
|
||||
logged bool
|
||||
expires time.Time
|
||||
}
|
||||
encodedUsers []encodedUser
|
||||
|
||||
basicAuthMiddleware struct {
|
||||
config config.BasicAuth
|
||||
// these are filled from the config.Users map at the startup
|
||||
auth encodedUsers
|
||||
realmHeaderValue string
|
||||
expireEnabled bool // if the config.Expires is a valid date, default disabled
|
||||
}
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
// New takes one parameter, the config.BasicAuth returns a HandlerFunc
|
||||
// use: iris.UseFunc(New(...)), iris.Get(...,New(...),...)
|
||||
func New(c config.BasicAuth) iris.HandlerFunc {
|
||||
return NewHandler(c).Serve
|
||||
}
|
||||
|
||||
// NewHandler takes one parameter, the config.BasicAuth returns a Handler
|
||||
// use: iris.Use(NewHandler(...)), iris.Get(...,iris.HandlerFunc(NewHandler(...)),...)
|
||||
func NewHandler(c config.BasicAuth) iris.Handler {
|
||||
b := &basicAuthMiddleware{config: config.DefaultBasicAuth().MergeSingle(c)}
|
||||
b.init()
|
||||
return b
|
||||
}
|
||||
|
||||
// Default takes one parameter, the users returns a HandlerFunc
|
||||
// use: iris.UseFunc(Default(...)), iris.Get(...,Default(...),...)
|
||||
func Default(users map[string]string) iris.HandlerFunc {
|
||||
return DefaultHandler(users).Serve
|
||||
}
|
||||
|
||||
// DefaultHandler takes one parameter, the users returns a Handler
|
||||
// use: iris.Use(DefaultHandler(...)), iris.Get(...,iris.HandlerFunc(Default(...)),...)
|
||||
func DefaultHandler(users map[string]string) iris.Handler {
|
||||
c := config.DefaultBasicAuth()
|
||||
c.Users = users
|
||||
return NewHandler(c)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func (b *basicAuthMiddleware) init() {
|
||||
// pass the encoded users from the user's config's Users value
|
||||
b.auth = make(encodedUsers, 0, len(b.config.Users))
|
||||
|
||||
for k, v := range b.config.Users {
|
||||
fullUser := k + ":" + v
|
||||
header := "Basic " + base64.StdEncoding.EncodeToString([]byte(fullUser))
|
||||
b.auth = append(b.auth, encodedUser{HeaderValue: header, Username: k, logged: false, expires: config.CookieExpireNever})
|
||||
}
|
||||
|
||||
// set the auth realm header's value
|
||||
b.realmHeaderValue = "Basic realm=" + strconv.Quote(b.config.Realm)
|
||||
|
||||
if b.config.Expires > 0 {
|
||||
b.expireEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, found bool) {
|
||||
if len(headerValue) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range b.auth {
|
||||
if user.HeaderValue == headerValue {
|
||||
auth = &user
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *basicAuthMiddleware) askForCredentials(ctx *iris.Context) {
|
||||
ctx.SetHeader("WWW-Authenticate", b.realmHeaderValue)
|
||||
ctx.SetStatusCode(iris.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// Serve the actual middleware
|
||||
func (b *basicAuthMiddleware) Serve(ctx *iris.Context) {
|
||||
|
||||
if auth, found := b.findAuth(ctx.RequestHeader("Authorization")); !found {
|
||||
/* I spent time for nothing
|
||||
if b.banEnabled && auth != nil { // this propably never work
|
||||
|
||||
if auth.tries == b.config.MaxTries {
|
||||
auth.bannedTime = time.Now()
|
||||
auth.unbanTime = time.Now().Add(b.config.BanDuration) // set the unban time
|
||||
auth.tries++ // we plus them in order to check if already banned later
|
||||
// client is banned send a forbidden status and don't continue
|
||||
ctx.SetStatusCode(iris.StatusForbidden)
|
||||
return
|
||||
} else if auth.tries > b.config.MaxTries { // it's already banned, so check the ban duration with the bannedTime
|
||||
if time.Now().After(auth.unbanTime) { // here we unban the client
|
||||
auth.tries = 0
|
||||
auth.bannedTime = config.CookieExpireNever
|
||||
auth.unbanTime = config.CookieExpireNever
|
||||
// continue and askCredentials as normal
|
||||
} else {
|
||||
// client is banned send a forbidden status and don't continue
|
||||
ctx.SetStatusCode(iris.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if auth != nil {
|
||||
auth.tries++
|
||||
}*/
|
||||
|
||||
b.askForCredentials(ctx)
|
||||
// don't continue to the next handler
|
||||
} else {
|
||||
// all ok set the context's value in order to be getable from the next handler
|
||||
ctx.Set(b.config.ContextKey, auth.Username)
|
||||
if b.expireEnabled {
|
||||
|
||||
if auth.logged == false {
|
||||
auth.expires = time.Now().Add(b.config.Expires)
|
||||
auth.logged = true
|
||||
}
|
||||
|
||||
if time.Now().After(auth.expires) {
|
||||
b.askForCredentials(ctx) // ask for authentication again
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//auth.tries = 0
|
||||
ctx.Next() // continue
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
## Middleware information
|
||||
|
||||
This is a fork of the CORS middleware from [here](https://github.com/rs/cors/)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
It does some security work for you between the requests, a brief view on what you can set:
|
||||
|
||||
* AllowedOrigins []string
|
||||
|
||||
* AllowOriginFunc func(origin string) bool
|
||||
|
||||
* AllowedMethods []string
|
||||
|
||||
* AllowedHeadersAll bool
|
||||
|
||||
* ExposedHeaders []string
|
||||
|
||||
* AllowCredentials bool
|
||||
|
||||
* MaxAge int
|
||||
|
||||
* OptionsPassthrough bool
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
```go
|
||||
|
||||
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
|
||||
// If the special "*" value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters
|
||||
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
|
||||
// Only one wildcard can be used per origin.
|
||||
// Default value is ["*"]
|
||||
AllowedOrigins []string
|
||||
// AllowOriginFunc is a custom function to validate the origin. It take the origin
|
||||
// as argument and returns true if allowed or false otherwise. If this option is
|
||||
// set, the content of AllowedOrigins is ignored.
|
||||
AllowOriginFunc func(origin string) bool
|
||||
// AllowedMethods is a list of methods the client is allowed to use with
|
||||
// cross-domain requests. Default value is simple methods (GET and POST)
|
||||
AllowedMethods []string
|
||||
// AllowedHeaders is list of non simple headers the client is allowed to use with
|
||||
// cross-domain requests.
|
||||
// If the special "*" value is present in the list, all headers will be allowed.
|
||||
// Default value is [] but "Origin" is always appended to the list.
|
||||
AllowedHeaders []string
|
||||
|
||||
AllowedHeadersAll bool
|
||||
|
||||
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
||||
// API specification
|
||||
ExposedHeaders []string
|
||||
// AllowCredentials indicates whether the request can include user credentials like
|
||||
// cookies, HTTP authentication or client side SSL certificates.
|
||||
AllowCredentials bool
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached
|
||||
MaxAge int
|
||||
// OptionsPassthrough instructs preflight to let other potential next handlers to
|
||||
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
|
||||
OptionsPassthrough bool
|
||||
// Debugging flag adds additional output to debug server side CORS issues
|
||||
Debug bool
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/cors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
//crs := cors.New(cors.Options{})
|
||||
|
||||
iris.Use(cors.Default()) // crs
|
||||
|
||||
iris.Get("/home", func(c *iris.Context) {
|
||||
c.Write("Hello from /home")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
@@ -1,405 +0,0 @@
|
||||
// Cors credits goes to @rs
|
||||
|
||||
package cors
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
const toLower = 'a' - 'A'
|
||||
|
||||
type converter func(string) string
|
||||
|
||||
type wildcard struct {
|
||||
prefix string
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (w wildcard) match(s string) bool {
|
||||
return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
|
||||
}
|
||||
|
||||
// convert converts a list of string using the passed converter function
|
||||
func convert(s []string, c converter) []string {
|
||||
out := []string{}
|
||||
for _, i := range s {
|
||||
out = append(out, c(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// parseHeaderList tokenize + normalize a string containing a list of headers
|
||||
func parseHeaderList(headerList string) []string {
|
||||
l := len(headerList)
|
||||
h := make([]byte, 0, l)
|
||||
upper := true
|
||||
// Estimate the number headers in order to allocate the right splice size
|
||||
t := 0
|
||||
for i := 0; i < l; i++ {
|
||||
if headerList[i] == ',' {
|
||||
t++
|
||||
}
|
||||
}
|
||||
headers := make([]string, 0, t)
|
||||
for i := 0; i < l; i++ {
|
||||
b := headerList[i]
|
||||
if b >= 'a' && b <= 'z' {
|
||||
if upper {
|
||||
h = append(h, b-toLower)
|
||||
} else {
|
||||
h = append(h, b)
|
||||
}
|
||||
} else if b >= 'A' && b <= 'Z' {
|
||||
if !upper {
|
||||
h = append(h, b+toLower)
|
||||
} else {
|
||||
h = append(h, b)
|
||||
}
|
||||
} else if b == '-' || b == '_' || (b >= '0' && b <= '9') {
|
||||
h = append(h, b)
|
||||
}
|
||||
|
||||
if b == ' ' || b == ',' || i == l-1 {
|
||||
if len(h) > 0 {
|
||||
// Flush the found header
|
||||
headers = append(headers, string(h))
|
||||
h = h[:0]
|
||||
upper = true
|
||||
}
|
||||
} else {
|
||||
upper = b == '-' || b == '_'
|
||||
}
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
// Options is a configuration container to setup the CORS middleware.
|
||||
type Options struct {
|
||||
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
|
||||
// If the special "*" value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters
|
||||
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
|
||||
// Only one wildcard can be used per origin.
|
||||
// Default value is ["*"]
|
||||
AllowedOrigins []string
|
||||
// AllowOriginFunc is a custom function to validate the origin. It take the origin
|
||||
// as argument and returns true if allowed or false otherwise. If this option is
|
||||
// set, the content of AllowedOrigins is ignored.
|
||||
AllowOriginFunc func(origin string) bool
|
||||
// AllowedMethods is a list of methods the client is allowed to use with
|
||||
// cross-domain requests. Default value is simple methods (GET and POST)
|
||||
AllowedMethods []string
|
||||
// AllowedHeaders is list of non simple headers the client is allowed to use with
|
||||
// cross-domain requests.
|
||||
// If the special "*" value is present in the list, all headers will be allowed.
|
||||
// Default value is [] but "Origin" is always appended to the list.
|
||||
AllowedHeaders []string
|
||||
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
||||
// API specification
|
||||
ExposedHeaders []string
|
||||
// AllowCredentials indicates whether the request can include user credentials like
|
||||
// cookies, HTTP authentication or client side SSL certificates.
|
||||
AllowCredentials bool
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached
|
||||
MaxAge int
|
||||
// OptionsPassthrough instructs preflight to let other potential next handlers to
|
||||
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
|
||||
OptionsPassthrough bool
|
||||
// Debugging flag adds additional output to debug server side CORS issues
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// Cors http handler
|
||||
type Cors struct {
|
||||
// Debug logger
|
||||
Log *log.Logger
|
||||
// Set to true when allowed origins contains a "*"
|
||||
allowedOriginsAll bool
|
||||
// Normalized list of plain allowed origins
|
||||
allowedOrigins []string
|
||||
// List of allowed origins containing wildcards
|
||||
allowedWOrigins []wildcard
|
||||
// Optional origin validator function
|
||||
allowOriginFunc func(origin string) bool
|
||||
// Set to true when allowed headers contains a "*"
|
||||
allowedHeadersAll bool
|
||||
// Normalized list of allowed headers
|
||||
allowedHeaders []string
|
||||
// Normalized list of allowed methods
|
||||
allowedMethods []string
|
||||
// Normalized list of exposed headers
|
||||
exposedHeaders []string
|
||||
allowCredentials bool
|
||||
maxAge int
|
||||
optionPassthrough bool
|
||||
}
|
||||
|
||||
// New creates a new Cors handler with the provided options.
|
||||
func New(options Options) *Cors {
|
||||
c := &Cors{
|
||||
exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
|
||||
allowOriginFunc: options.AllowOriginFunc,
|
||||
allowCredentials: options.AllowCredentials,
|
||||
maxAge: options.MaxAge,
|
||||
optionPassthrough: options.OptionsPassthrough,
|
||||
}
|
||||
if options.Debug {
|
||||
c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
|
||||
}
|
||||
|
||||
// Normalize options
|
||||
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
|
||||
// As it may error prone, we chose to ignore the spec here.
|
||||
|
||||
// Allowed Origins
|
||||
if len(options.AllowedOrigins) == 0 {
|
||||
// Default is all origins
|
||||
c.allowedOriginsAll = true
|
||||
} else {
|
||||
c.allowedOrigins = []string{}
|
||||
c.allowedWOrigins = []wildcard{}
|
||||
for _, origin := range options.AllowedOrigins {
|
||||
// Normalize
|
||||
origin = strings.ToLower(origin)
|
||||
if origin == "*" {
|
||||
// If "*" is present in the list, turn the whole list into a match all
|
||||
c.allowedOriginsAll = true
|
||||
c.allowedOrigins = nil
|
||||
c.allowedWOrigins = nil
|
||||
break
|
||||
} else if i := strings.IndexByte(origin, '*'); i >= 0 {
|
||||
// Split the origin in two: start and end string without the *
|
||||
w := wildcard{origin[0:i], origin[i+1:]}
|
||||
c.allowedWOrigins = append(c.allowedWOrigins, w)
|
||||
} else {
|
||||
c.allowedOrigins = append(c.allowedOrigins, origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allowed Headers
|
||||
if len(options.AllowedHeaders) == 0 {
|
||||
// Use sensible defaults
|
||||
c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
|
||||
} else {
|
||||
// Origin is always appended as some browsers will always request for this header at preflight
|
||||
c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
|
||||
for _, h := range options.AllowedHeaders {
|
||||
if h == "*" {
|
||||
c.allowedHeadersAll = true
|
||||
c.allowedHeaders = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allowed Methods
|
||||
if len(options.AllowedMethods) == 0 {
|
||||
// Default is spec's "simple" methods
|
||||
c.allowedMethods = []string{"GET", "POST"}
|
||||
} else {
|
||||
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Default creates a new Cors handler with default options
|
||||
func Default() *Cors {
|
||||
return New(Options{})
|
||||
}
|
||||
|
||||
// DefaultCors creates a new Cors handler with default options
|
||||
func DefaultCors() *Cors {
|
||||
return Default()
|
||||
}
|
||||
|
||||
// Conflicts used by the router optimizer
|
||||
func (c *Cors) Conflicts() string {
|
||||
return "httpmethod"
|
||||
}
|
||||
|
||||
// Serve serves the middleware
|
||||
func (c *Cors) Serve(ctx *iris.Context) {
|
||||
if ctx.MethodString() == iris.MethodOptions {
|
||||
c.logf("Serve: Preflight request")
|
||||
c.handlePreflight(ctx)
|
||||
// Preflight requests are standalone and should stop the chain as some other
|
||||
// middleware may not handle OPTIONS requests correctly. One typical example
|
||||
// is authentication middleware ; OPTIONS requests won't carry authentication
|
||||
// headers (see #1)
|
||||
if c.optionPassthrough {
|
||||
ctx.Next()
|
||||
}
|
||||
} else {
|
||||
c.logf("Serve: Actual request")
|
||||
c.handleActualRequest(ctx)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// handlePreflight handles pre-flight CORS requests
|
||||
func (c *Cors) handlePreflight(ctx *iris.Context) {
|
||||
origin := ctx.RequestHeader("Origin")
|
||||
|
||||
if ctx.MethodString() != iris.MethodOptions {
|
||||
c.logf(" Preflight aborted: %s!=OPTIONS", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
// Always set Vary headers
|
||||
ctx.Response.Header.Add("Vary", "Origin")
|
||||
ctx.Response.Header.Add("Vary", "Access-Control-Request-Method")
|
||||
ctx.Response.Header.Add("Vary", "Access-Control-Request-Headers")
|
||||
|
||||
if origin == "" {
|
||||
c.logf(" Preflight aborted: empty origin")
|
||||
return
|
||||
}
|
||||
if !c.isOriginAllowed(origin) {
|
||||
c.logf(" Preflight aborted: origin '%s' not allowed", origin)
|
||||
return
|
||||
}
|
||||
|
||||
reqMethod := ctx.RequestHeader("Access-Control-Request-Method")
|
||||
if !c.isMethodAllowed(reqMethod) {
|
||||
c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
|
||||
return
|
||||
}
|
||||
reqHeaders := parseHeaderList(ctx.RequestHeader("Access-Control-Request-Headers"))
|
||||
if !c.areHeadersAllowed(reqHeaders) {
|
||||
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
|
||||
return
|
||||
}
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
|
||||
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
|
||||
// by Access-Control-Request-Method (if supported) can be enough
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
|
||||
if len(reqHeaders) > 0 {
|
||||
|
||||
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
|
||||
// from Access-Control-Request-Headers can be enough
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
|
||||
}
|
||||
if c.allowCredentials {
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
if c.maxAge > 0 {
|
||||
ctx.Response.Header.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
|
||||
}
|
||||
c.logf(" Preflight response headers: %v", ctx.Response.Header)
|
||||
}
|
||||
|
||||
// handleActualRequest handles simple cross-origin requests, actual request or redirects
|
||||
func (c *Cors) handleActualRequest(ctx *iris.Context) {
|
||||
origin := ctx.RequestHeader("Origin")
|
||||
|
||||
if ctx.MethodString() == "OPTIONS" {
|
||||
c.logf(" Actual request no headers added: method == %s", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Response.Header.Add("Vary", "Origin")
|
||||
if origin == "" {
|
||||
c.logf(" Actual request no headers added: missing origin")
|
||||
return
|
||||
}
|
||||
if !c.isOriginAllowed(origin) {
|
||||
c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
|
||||
return
|
||||
}
|
||||
|
||||
// Note that spec does define a way to specifically disallow a simple method like GET or
|
||||
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
|
||||
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
|
||||
// We think it's a nice feature to be able to have control on those methods though.
|
||||
if !c.isMethodAllowed(ctx.MethodString()) {
|
||||
c.logf(" Actual request no headers added: method '%s' not allowed", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
|
||||
if len(c.exposedHeaders) > 0 {
|
||||
ctx.Response.Header.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
|
||||
}
|
||||
if c.allowCredentials {
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
c.logf(" Actual response added headers: %v", ctx.Response.Header)
|
||||
}
|
||||
|
||||
// convenience method. checks if debugging is turned on before printing
|
||||
func (c *Cors) logf(format string, a ...interface{}) {
|
||||
if c.Log != nil {
|
||||
c.Log.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
|
||||
// on the endpoint
|
||||
func (c *Cors) isOriginAllowed(origin string) bool {
|
||||
if c.allowOriginFunc != nil {
|
||||
return c.allowOriginFunc(origin)
|
||||
}
|
||||
if c.allowedOriginsAll {
|
||||
return true
|
||||
}
|
||||
origin = strings.ToLower(origin)
|
||||
for _, o := range c.allowedOrigins {
|
||||
if o == origin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, w := range c.allowedWOrigins {
|
||||
if w.match(origin) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
|
||||
// on the endpoing
|
||||
func (c *Cors) isMethodAllowed(method string) bool {
|
||||
if len(c.allowedMethods) == 0 {
|
||||
// If no method allowed, always return false, even for preflight request
|
||||
return false
|
||||
}
|
||||
method = strings.ToUpper(method)
|
||||
if method == "OPTIONS" {
|
||||
// Always allow preflight requests
|
||||
return true
|
||||
}
|
||||
for _, m := range c.allowedMethods {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// areHeadersAllowed checks if a given list of headers are allowed to used within
|
||||
// a cross-domain request.
|
||||
func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
|
||||
if c.allowedHeadersAll || len(requestedHeaders) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, header := range requestedHeaders {
|
||||
header = http.CanonicalHeaderKey(header)
|
||||
found := false
|
||||
for _, h := range c.allowedHeaders {
|
||||
if h == header {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
## Middleware information
|
||||
|
||||
This folder contains a middleware for internationalization uses a third-party package named i81n.
|
||||
|
||||
More can be found here:
|
||||
[https://github.com/Unknwon/i18n](https://github.com/Unknwon/i18n)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Package i18n is for app Internationalization and Localization.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
Create folder named 'locales'
|
||||
```
|
||||
///Files:
|
||||
|
||||
./locales/locale_en-US.ini
|
||||
./locales/locale_el-US.ini
|
||||
```
|
||||
Contents on locale_en-US:
|
||||
```
|
||||
hi = hello, %s
|
||||
```
|
||||
Contents on locale_el-GR:
|
||||
```
|
||||
hi = Ãåéá, %s
|
||||
```
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/i18n"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
iris.UseFunc(i18n.I18n(i18n.Options{Default: "en-US",
|
||||
Languages: map[string]string{
|
||||
"en-US": "./locales/locale_en-US.ini",
|
||||
"el-GR": "./locales/locale_el-GR.ini",
|
||||
"zh-CN": "./locales/locale_zh-CN.ini"}}))
|
||||
// or iris.Use(i18n.I18nHandler(....))
|
||||
// or iris.Get("/",i18n.I18n(....), func (ctx *iris.Context){})
|
||||
|
||||
iris.Get("/", func(ctx *iris.Context) {
|
||||
hi := ctx.GetFmt("translate")("hi", "maki") // hi is the key, 'maki' is the %s, the second parameter is optional
|
||||
language := ctx.Get("language") // language is the language key, example 'en-US'
|
||||
|
||||
ctx.Write("From the language %s translated output: %s", language, hi)
|
||||
})
|
||||
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### [For a working example, click here](https://github.com/kataras/iris/tree/examples/middleware_internationalization_i18n)
|
||||
@@ -1,99 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/i18n"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
// AcceptLanguage is the Header key "Accept-Language"
|
||||
const AcceptLanguage = "Accept-Language"
|
||||
|
||||
// Options the i18n options
|
||||
type Options struct {
|
||||
// Default set it if you want a default language
|
||||
//
|
||||
// Checked: Configuration state, not at runtime
|
||||
Default string
|
||||
// URLParameter is the name of the url parameter which the language can be indentified
|
||||
//
|
||||
// Checked: Serving state, runtime
|
||||
URLParameter string
|
||||
// Languages is a map[string]string which the key is the language i81n and the value is the file location
|
||||
//
|
||||
// Example of key is: 'en-US'
|
||||
// Example of value is: './locales/en-US.ini'
|
||||
Languages map[string]string
|
||||
}
|
||||
|
||||
type i18nMiddleware struct {
|
||||
options Options
|
||||
}
|
||||
|
||||
func (i *i18nMiddleware) Serve(ctx *iris.Context) {
|
||||
wasByCookie := false
|
||||
// try to get by url parameter
|
||||
language := ctx.URLParam(i.options.URLParameter)
|
||||
|
||||
if language == "" {
|
||||
// then try to take the lang field from the cookie
|
||||
language = ctx.GetCookie("lang")
|
||||
|
||||
if len(language) > 0 {
|
||||
wasByCookie = true
|
||||
} else {
|
||||
// try to get by the request headers(?)
|
||||
if langHeader := ctx.RequestHeader(AcceptLanguage); i18n.IsExist(langHeader) {
|
||||
language = langHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it was not taken by the cookie, then set the cookie in order to have it
|
||||
if !wasByCookie {
|
||||
ctx.SetCookieKV("language", language)
|
||||
}
|
||||
if language == "" {
|
||||
language = i.options.Default
|
||||
}
|
||||
locale := i18n.Locale{language}
|
||||
ctx.Set("language", language)
|
||||
ctx.Set("translate", locale.Tr)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// I18nHandler returns the middleware which is just an iris.handler
|
||||
func I18nHandler(_options ...Options) *i18nMiddleware {
|
||||
i := &i18nMiddleware{}
|
||||
if len(_options) == 0 || (len(_options) > 0 && len(_options[0].Languages) == 0) {
|
||||
panic("You cannot use this middleware without set the Languages option, please try again and read the docs.")
|
||||
}
|
||||
|
||||
i.options = _options[0]
|
||||
firstlanguage := ""
|
||||
//load the files
|
||||
for k, v := range i.options.Languages {
|
||||
if !strings.HasSuffix(v, ".ini") {
|
||||
v += ".ini"
|
||||
}
|
||||
err := i18n.SetMessage(k, v)
|
||||
if err != nil && err != i18n.ErrLangAlreadyExist {
|
||||
panic("Iris i18n Middleware: Failed to set locale file" + k + " Error:" + err.Error())
|
||||
}
|
||||
if firstlanguage == "" {
|
||||
firstlanguage = k
|
||||
}
|
||||
}
|
||||
// if not default language setted then set to the first of the i.options.Languages
|
||||
if i.options.Default == "" {
|
||||
i.options.Default = firstlanguage
|
||||
}
|
||||
|
||||
i18n.SetDefaultLang(i.options.Default)
|
||||
return i
|
||||
}
|
||||
|
||||
// I18n returns the middleware as iris.HandlerFunc with the passed options
|
||||
func I18n(_options ...Options) iris.HandlerFunc {
|
||||
return I18nHandler(_options...).Serve
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
## Middleware information
|
||||
|
||||
This folder contains a middleware which is a bridge between Iris station's logger and http requests.
|
||||
|
||||
**Logs the incoming requests**
|
||||
|
||||
## How to use
|
||||
|
||||
Read the logger section [here](https://kataras.gitbooks.io/iris/content/logger.html)
|
||||
@@ -1,100 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
)
|
||||
|
||||
// Options are the options of the logger middlweare
|
||||
// contains 5 bools
|
||||
// Status, IP, Method, Path, EnableColors
|
||||
// if set to true then these will print
|
||||
type Options struct {
|
||||
// Status displays status code (bool)
|
||||
Status bool
|
||||
// IP displays request's remote address (bool)
|
||||
IP bool
|
||||
// Method displays the http method (bool)
|
||||
Method bool
|
||||
// Path displays the request path (bool)
|
||||
Path bool
|
||||
// EnableColors defaults to false
|
||||
EnableColors bool
|
||||
}
|
||||
|
||||
// DefaultOptions returns an options which all properties are true
|
||||
func DefaultOptions() Options {
|
||||
return Options{true, true, true, true, false}
|
||||
}
|
||||
|
||||
type loggerMiddleware struct {
|
||||
*logger.Logger
|
||||
options Options
|
||||
}
|
||||
|
||||
// Serve serves the middleware
|
||||
func (l *loggerMiddleware) Serve(ctx *iris.Context) {
|
||||
//all except latency to string
|
||||
var date, status, ip, method, path string
|
||||
var latency time.Duration
|
||||
var startTime, endTime time.Time
|
||||
path = ctx.PathString()
|
||||
method = ctx.MethodString()
|
||||
|
||||
startTime = time.Now()
|
||||
|
||||
ctx.Next()
|
||||
//no time.Since in order to format it well after
|
||||
endTime = time.Now()
|
||||
date = endTime.Format("01/02 - 15:04:05")
|
||||
latency = endTime.Sub(startTime)
|
||||
|
||||
if l.options.Status {
|
||||
status = strconv.Itoa(ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
if l.options.IP {
|
||||
ip = ctx.RemoteAddr()
|
||||
}
|
||||
|
||||
if !l.options.Method {
|
||||
method = ""
|
||||
}
|
||||
|
||||
if !l.options.Path {
|
||||
path = ""
|
||||
}
|
||||
|
||||
//finally print the logs
|
||||
l.printf("%s %v %4v %s %s %s \n", date, status, latency, ip, method, path)
|
||||
|
||||
}
|
||||
|
||||
func (l *loggerMiddleware) printf(format string, a ...interface{}) {
|
||||
if l.options.EnableColors {
|
||||
l.Logger.Otherf(format, a...)
|
||||
} else {
|
||||
l.Logger.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns the logger middleware as HandlerFunc with the default settings if second parameter is not passed
|
||||
func New(theLogger *logger.Logger, options ...Options) iris.HandlerFunc {
|
||||
if theLogger == nil {
|
||||
theLogger = logger.New(config.DefaultLogger())
|
||||
}
|
||||
|
||||
l := &loggerMiddleware{Logger: theLogger}
|
||||
|
||||
if len(options) > 0 {
|
||||
l.options = options[0]
|
||||
} else {
|
||||
l.options = DefaultOptions()
|
||||
}
|
||||
|
||||
return l.Serve
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
## Middleware information
|
||||
|
||||
This folder contains a middleware for safety recover the server from panic
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/recovery"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
iris.Use(recovery.New(os.Stderr)) // optional parameter is the writer which the stack of the panic will be printed
|
||||
|
||||
iris.Get("/", func(ctx *iris.Context) {
|
||||
ctx.Write("Hi, let's panic")
|
||||
panic("errorrrrrrrrrrrrrrr")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
}
|
||||
|
||||
```
|
||||
@@ -1,45 +0,0 @@
|
||||
package recovery
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type recovery struct {
|
||||
//out optional output to log any panics
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (r recovery) Serve(ctx *iris.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
r.out.Write([]byte("[" + time.Now().String() + "]Recovery from panic \n"))
|
||||
//ctx.Panic just sends http status 500 by default, but you can change it by: iris.OnPanic(func( c *iris.Context){})
|
||||
ctx.Panic()
|
||||
}
|
||||
}()
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// Recovery restores the server on internal server errors (panics)
|
||||
// receives an optional writer, the default is the os.Stderr if no out writer given
|
||||
// returns the middleware as iris.Handler
|
||||
// same as New(...)
|
||||
func Recovery(out ...io.Writer) iris.Handler {
|
||||
r := recovery{os.Stderr}
|
||||
if out != nil && len(out) == 1 {
|
||||
r.out = out[0]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// New restores the server on internal server errors (panics)
|
||||
// receives an optional writer, the default is the os.Stderr if no out writer given
|
||||
// returns the middleware as iris.Handler
|
||||
// same as Recovery(...)
|
||||
func New(out ...io.Writer) iris.Handler {
|
||||
return Recovery(out...)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
## Middleware information
|
||||
|
||||
This was out-of-the-box iris supported before, but after Iris V1.1.0 it's not, so I had to modify it.
|
||||
|
||||
|
||||
This folder contains a middleware ported to Iris from a third-party middleware named secure.
|
||||
|
||||
More can be found here:
|
||||
[https://github.com/unrolled/secure](https://github.com/unrolled/secure)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Secure is an HTTP middleware for Go that facilitates some quick security wins.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/secure"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := secure.New(secure.Options{
|
||||
AllowedHosts: []string{"ssl.example.com"}, // AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
|
||||
SSLRedirect: true, // If SSLRedirect is set to true, then only allow HTTPS requests. Default is false.
|
||||
SSLTemporaryRedirect: false, // If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
|
||||
SSLHost: "ssl.example.com", // SSLHost is the host name that is used to redirect HTTP requests to HTTPS. Default is "", which indicates to use the same host.
|
||||
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // SSLProxyHeaders is set of header keys with associated values that would indicate a valid HTTPS request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
|
||||
STSSeconds: 315360000, // STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
STSIncludeSubdomains: true, // If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSPreload: true, // If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
|
||||
ForceSTSHeader: false, // STS header is only included when the connection is HTTPS. If you want to force it to always be added, set to true. `IsDevelopment` still overrides this. Default is false.
|
||||
FrameDeny: true, // If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
|
||||
CustomFrameOptionsValue: "SAMEORIGIN", // CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
|
||||
ContentTypeNosniff: true, // If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
|
||||
BrowserXSSFilter: true, // If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
|
||||
ContentSecurityPolicy: "default-src 'self'", // ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
PublicKey: `pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubdomains; report-uri="https://www.example.com/hpkp-report"`, // PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
|
||||
|
||||
IsDevelopment: true, // This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
|
||||
})
|
||||
|
||||
iris.UseFunc(func(c *iris.Context) {
|
||||
err := s.Process(c)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
|
||||
iris.Get("/home", func(c *iris.Context) {
|
||||
c.Write("Hello from /home")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
This has been modified to work with Iris, credits goes to https://github.com/unrolled/secure
|
||||
*/
|
||||
|
||||
package secure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
const (
|
||||
stsHeader = "Strict-Transport-Security"
|
||||
stsSubdomainString = "; includeSubdomains"
|
||||
stsPreloadString = "; preload"
|
||||
frameOptionsHeader = "X-Frame-Options"
|
||||
frameOptionsValue = "DENY"
|
||||
contentTypeHeader = "X-Content-Type-Options"
|
||||
contentTypeValue = "nosniff"
|
||||
xssProtectionHeader = "X-XSS-Protection"
|
||||
xssProtectionValue = "1; mode=block"
|
||||
cspHeader = "Content-Security-Policy"
|
||||
hpkpHeader = "Public-Key-Pins"
|
||||
)
|
||||
|
||||
func defaultBadHostHandler(ctx *iris.Context) {
|
||||
ctx.Text(iris.StatusInternalServerError, "Bad Host")
|
||||
}
|
||||
|
||||
// Options is a struct for specifying configuration options for the secure.Secure middleware.
|
||||
type Options struct {
|
||||
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
|
||||
AllowedHosts []string
|
||||
// If SSLRedirect is set to true, then only allow https requests. Default is false.
|
||||
SSLRedirect bool
|
||||
// If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
|
||||
SSLTemporaryRedirect bool
|
||||
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
|
||||
SSLHost string
|
||||
// SSLProxyHeaders is set of header keys with associated values that would indicate a valid https request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
|
||||
SSLProxyHeaders map[string]string
|
||||
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
STSSeconds int64
|
||||
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSIncludeSubdomains bool
|
||||
// If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSPreload bool
|
||||
// If ForceSTSHeader is set to true, the STS header will be added even when the connection is HTTP. Default is false.
|
||||
ForceSTSHeader bool
|
||||
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
|
||||
FrameDeny bool
|
||||
// CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
|
||||
CustomFrameOptionsValue string
|
||||
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
|
||||
ContentTypeNosniff bool
|
||||
// BrowserXSSFilter If it's true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
|
||||
BrowserXSSFilter bool
|
||||
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
ContentSecurityPolicy string
|
||||
// PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
|
||||
PublicKey string
|
||||
// When developing, the AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain... so set this to true for dev environment.
|
||||
// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. Default if false.
|
||||
IsDevelopment bool
|
||||
}
|
||||
|
||||
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
|
||||
// provided to configure which features should be enabled, and the ability to override a few of the default values.
|
||||
type Secure struct {
|
||||
// Customize Secure with an Options struct.
|
||||
opt Options
|
||||
|
||||
// Handlers for when an error occurs (ie bad host).
|
||||
badHostHandler iris.Handler
|
||||
}
|
||||
|
||||
// New constructs a new Secure instance with supplied options.
|
||||
func New(options ...Options) *Secure {
|
||||
var o Options
|
||||
if len(options) == 0 {
|
||||
o = Options{}
|
||||
} else {
|
||||
o = options[0]
|
||||
}
|
||||
|
||||
return &Secure{
|
||||
opt: o,
|
||||
badHostHandler: iris.HandlerFunc(defaultBadHostHandler),
|
||||
}
|
||||
}
|
||||
|
||||
// SetBadHostHandler sets the handler to call when secure rejects the host name.
|
||||
func (s *Secure) SetBadHostHandler(handler iris.Handler) {
|
||||
s.badHostHandler = handler
|
||||
}
|
||||
|
||||
// Handler implements the iris.HandlerFunc for integration with iris.
|
||||
func (s *Secure) Handler(h iris.Handler) iris.Handler {
|
||||
return iris.HandlerFunc(func(ctx *iris.Context) {
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
err := s.Process(ctx)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h.Serve(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Process runs the actual checks and returns an error if the middleware chain should stop.
|
||||
func (s *Secure) Process(ctx *iris.Context) error {
|
||||
// Allowed hosts check.
|
||||
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
|
||||
isGoodHost := false
|
||||
for _, allowedHost := range s.opt.AllowedHosts {
|
||||
if strings.EqualFold(allowedHost, string(ctx.Host())) {
|
||||
isGoodHost = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isGoodHost {
|
||||
s.badHostHandler.Serve(ctx)
|
||||
return fmt.Errorf("Bad host name: %s", string(ctx.Host()))
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we are on HTTPS.
|
||||
isSSL := strings.EqualFold(string(ctx.Request.URI().Scheme()), "https") || ctx.IsTLS()
|
||||
if !isSSL {
|
||||
for k, v := range s.opt.SSLProxyHeaders {
|
||||
if ctx.RequestHeader(k) == v {
|
||||
isSSL = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SSL check.
|
||||
if s.opt.SSLRedirect && !isSSL && !s.opt.IsDevelopment {
|
||||
url := ctx.Request.URI()
|
||||
url.SetScheme("https")
|
||||
url.SetHostBytes(ctx.Host())
|
||||
|
||||
if len(s.opt.SSLHost) > 0 {
|
||||
url.SetHost(s.opt.SSLHost)
|
||||
}
|
||||
|
||||
status := iris.StatusMovedPermanently
|
||||
if s.opt.SSLTemporaryRedirect {
|
||||
status = iris.StatusTemporaryRedirect
|
||||
}
|
||||
|
||||
ctx.Redirect(url.String(), status)
|
||||
return fmt.Errorf("Redirecting to HTTPS")
|
||||
}
|
||||
|
||||
// Strict Transport Security header. Only add header when we know it's an SSL connection.
|
||||
// See https://tools.ietf.org/html/rfc6797#section-7.2 for details.
|
||||
if s.opt.STSSeconds != 0 && (isSSL || s.opt.ForceSTSHeader) && !s.opt.IsDevelopment {
|
||||
stsSub := ""
|
||||
if s.opt.STSIncludeSubdomains {
|
||||
stsSub = stsSubdomainString
|
||||
}
|
||||
|
||||
if s.opt.STSPreload {
|
||||
stsSub += stsPreloadString
|
||||
}
|
||||
|
||||
ctx.Response.Header.Add(stsHeader, fmt.Sprintf("max-age=%d%s", s.opt.STSSeconds, stsSub))
|
||||
}
|
||||
|
||||
// Frame Options header.
|
||||
if len(s.opt.CustomFrameOptionsValue) > 0 {
|
||||
ctx.Response.Header.Add(frameOptionsHeader, s.opt.CustomFrameOptionsValue)
|
||||
} else if s.opt.FrameDeny {
|
||||
ctx.Response.Header.Add(frameOptionsHeader, frameOptionsValue)
|
||||
}
|
||||
|
||||
// Content Type Options header.
|
||||
if s.opt.ContentTypeNosniff {
|
||||
ctx.Response.Header.Add(contentTypeHeader, contentTypeValue)
|
||||
}
|
||||
|
||||
// XSS Protection header.
|
||||
if s.opt.BrowserXSSFilter {
|
||||
ctx.Response.Header.Add(xssProtectionHeader, xssProtectionValue)
|
||||
}
|
||||
|
||||
// HPKP header.
|
||||
if len(s.opt.PublicKey) > 0 && isSSL && !s.opt.IsDevelopment {
|
||||
ctx.Response.Header.Add(hpkpHeader, s.opt.PublicKey)
|
||||
}
|
||||
|
||||
// Content Security Policy header.
|
||||
if len(s.opt.ContentSecurityPolicy) > 0 {
|
||||
ctx.Response.Header.Add(cspHeader, s.opt.ContentSecurityPolicy)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user