github.com/kotovmak/go-admin@v1.1.1/modules/auth/middleware.go (about)

     1  // Copyright 2019 GoAdmin Core Team. All rights reserved.
     2  // Use of this source code is governed by a Apache-2.0 style
     3  // license that can be found in the LICENSE file.
     4  
     5  package auth
     6  
     7  import (
     8  	"net/http"
     9  	"net/url"
    10  
    11  	"github.com/kotovmak/go-admin/context"
    12  	"github.com/kotovmak/go-admin/modules/config"
    13  	"github.com/kotovmak/go-admin/modules/constant"
    14  	"github.com/kotovmak/go-admin/modules/db"
    15  	"github.com/kotovmak/go-admin/modules/errors"
    16  	"github.com/kotovmak/go-admin/modules/language"
    17  	"github.com/kotovmak/go-admin/modules/logger"
    18  	"github.com/kotovmak/go-admin/modules/page"
    19  	"github.com/kotovmak/go-admin/plugins/admin/models"
    20  	template2 "github.com/kotovmak/go-admin/template"
    21  	"github.com/kotovmak/go-admin/template/types"
    22  )
    23  
    24  // Invoker contains the callback functions which are used
    25  // in the route middleware.
    26  type Invoker struct {
    27  	prefix                 string
    28  	authFailCallback       MiddlewareCallback
    29  	permissionDenyCallback MiddlewareCallback
    30  	conn                   db.Connection
    31  }
    32  
    33  // Middleware is the default auth middleware of plugins.
    34  func Middleware(conn db.Connection) context.Handler {
    35  	return DefaultInvoker(conn).Middleware()
    36  }
    37  
    38  // DefaultInvoker return a default Invoker.
    39  func DefaultInvoker(conn db.Connection) *Invoker {
    40  	return &Invoker{
    41  		prefix: config.Prefix(),
    42  		authFailCallback: func(ctx *context.Context) {
    43  			if ctx.Request.URL.Path == config.GetLoginUrl() {
    44  				return
    45  			}
    46  			if ctx.Request.URL.Path == "/logout" {
    47  				ctx.Write(302, map[string]string{
    48  					"Location": config.GetLoginUrl(),
    49  				}, ``)
    50  				return
    51  			}
    52  			param := ""
    53  			if ref := ctx.Referer(); ref != "" {
    54  				param = "?ref=" + url.QueryEscape(ref)
    55  			}
    56  
    57  			u := config.GetLoginUrl() + param
    58  			_, err := ctx.Request.Cookie(DefaultCookieKey)
    59  			referer := ctx.Referer()
    60  
    61  			if (ctx.Headers(constant.PjaxHeader) == "" && ctx.Method() != "GET") ||
    62  				err != nil ||
    63  				referer == "" {
    64  				ctx.Write(302, map[string]string{
    65  					"Location": u,
    66  				}, ``)
    67  			} else {
    68  				msg := language.Get("login overdue, please login again")
    69  				ctx.HTML(http.StatusOK, `<script>
    70  	if (typeof(swal) === "function") {
    71  		swal({
    72  			type: "info",
    73  			title: "`+language.Get("login info")+`",
    74  			text: "`+msg+`",
    75  			showCancelButton: false,
    76  			confirmButtonColor: "#3c8dbc",
    77  			confirmButtonText: '`+language.Get("got it")+`',
    78          })
    79  		setTimeout(function(){ location.href = "`+u+`"; }, 3000);
    80  	} else {
    81  		alert("`+msg+`")
    82  		location.href = "`+u+`"
    83      }
    84  </script>`)
    85  			}
    86  		},
    87  		permissionDenyCallback: func(ctx *context.Context) {
    88  			if ctx.Headers(constant.PjaxHeader) == "" && ctx.Method() != "GET" {
    89  				ctx.JSON(http.StatusForbidden, map[string]interface{}{
    90  					"code": http.StatusForbidden,
    91  					"msg":  language.Get(errors.PermissionDenied),
    92  				})
    93  			} else {
    94  				page.SetPageContent(ctx, Auth(ctx), func(ctx interface{}) (types.Panel, error) {
    95  					return template2.WarningPanel(errors.PermissionDenied, template2.NoPermission403Page), nil
    96  				}, conn)
    97  			}
    98  		},
    99  		conn: conn,
   100  	}
   101  }
   102  
   103  // SetPrefix return the default Invoker with the given prefix.
   104  func SetPrefix(prefix string, conn db.Connection) *Invoker {
   105  	i := DefaultInvoker(conn)
   106  	i.prefix = prefix
   107  	return i
   108  }
   109  
   110  // SetAuthFailCallback set the authFailCallback of Invoker.
   111  func (invoker *Invoker) SetAuthFailCallback(callback MiddlewareCallback) *Invoker {
   112  	invoker.authFailCallback = callback
   113  	return invoker
   114  }
   115  
   116  // SetPermissionDenyCallback set the permissionDenyCallback of Invoker.
   117  func (invoker *Invoker) SetPermissionDenyCallback(callback MiddlewareCallback) *Invoker {
   118  	invoker.permissionDenyCallback = callback
   119  	return invoker
   120  }
   121  
   122  // MiddlewareCallback is type of callback function.
   123  type MiddlewareCallback func(ctx *context.Context)
   124  
   125  // Middleware get the auth middleware from Invoker.
   126  func (invoker *Invoker) Middleware() context.Handler {
   127  	return func(ctx *context.Context) {
   128  		user, authOk, permissionOk := Filter(ctx, invoker.conn)
   129  
   130  		if authOk && permissionOk {
   131  			ctx.SetUserValue("user", user)
   132  			ctx.Next()
   133  			return
   134  		}
   135  
   136  		if !authOk {
   137  			invoker.authFailCallback(ctx)
   138  			ctx.Abort()
   139  			return
   140  		}
   141  
   142  		if !permissionOk {
   143  			ctx.SetUserValue("user", user)
   144  			invoker.permissionDenyCallback(ctx)
   145  			ctx.Abort()
   146  			return
   147  		}
   148  	}
   149  }
   150  
   151  // Filter retrieve the user model from Context and check the permission
   152  // at the same time.
   153  func Filter(ctx *context.Context, conn db.Connection) (models.UserModel, bool, bool) {
   154  	var (
   155  		id float64
   156  		ok bool
   157  
   158  		user     = models.User()
   159  		ses, err = InitSession(ctx, conn)
   160  	)
   161  
   162  	if err != nil {
   163  		logger.Error("retrieve auth user failed", err)
   164  		return user, false, false
   165  	}
   166  
   167  	if id, ok = ses.Get("user_id").(float64); !ok {
   168  		return user, false, false
   169  	}
   170  
   171  	user, ok = GetCurUserByID(int64(id), conn)
   172  
   173  	if !ok {
   174  		return user, false, false
   175  	}
   176  
   177  	return user, true, CheckPermissions(user, ctx.Request.URL.String(), ctx.Method(), ctx.PostForm())
   178  }
   179  
   180  const defaultUserIDSesKey = "user_id"
   181  
   182  // GetUserID return the user id from the session.
   183  func GetUserID(sesKey string, conn db.Connection) int64 {
   184  	id, err := GetSessionByKey(sesKey, defaultUserIDSesKey, conn)
   185  	if err != nil {
   186  		logger.Error("retrieve auth user failed", err)
   187  		return -1
   188  	}
   189  	if idFloat64, ok := id.(float64); ok {
   190  		return int64(idFloat64)
   191  	}
   192  	return -1
   193  }
   194  
   195  // GetCurUser return the user model.
   196  func GetCurUser(sesKey string, conn db.Connection) (user models.UserModel, ok bool) {
   197  
   198  	if sesKey == "" {
   199  		ok = false
   200  		return
   201  	}
   202  
   203  	id := GetUserID(sesKey, conn)
   204  	if id == -1 {
   205  		ok = false
   206  		return
   207  	}
   208  	return GetCurUserByID(id, conn)
   209  }
   210  
   211  // GetCurUserByID return the user model of given user id.
   212  func GetCurUserByID(id int64, conn db.Connection) (user models.UserModel, ok bool) {
   213  
   214  	user = models.User().SetConn(conn).Find(id)
   215  
   216  	if user.IsEmpty() {
   217  		ok = false
   218  		return
   219  	}
   220  
   221  	if user.Avatar == "" || config.GetStore().Prefix == "" {
   222  		user.Avatar = ""
   223  	} else {
   224  		user.Avatar = config.GetStore().URL(user.Avatar)
   225  	}
   226  
   227  	user = user.WithRoles().WithPermissions().WithMenus()
   228  
   229  	ok = user.HasMenu()
   230  
   231  	return
   232  }
   233  
   234  // CheckPermissions check the permission of the user.
   235  func CheckPermissions(user models.UserModel, path, method string, param url.Values) bool {
   236  	return user.CheckPermissionByUrlMethod(path, method, param)
   237  }