2017-04-12 08:27:57 -04:00
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
2017-01-30 08:30:02 -05:00
// See License.txt for license information.
package api4
import (
"fmt"
"net/http"
2017-04-26 23:11:32 +09:00
"regexp"
2017-01-30 08:30:02 -05:00
"strings"
"time"
l4g "github.com/alecthomas/log4go"
goi18n "github.com/nicksnyder/go-i18n/i18n"
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
2017-01-30 08:30:02 -05:00
)
type Context struct {
2017-09-06 17:12:54 -05:00
App * app . App
2017-04-04 11:54:52 -04:00
Session model . Session
Params * ApiParams
Err * model . AppError
T goi18n . TranslateFunc
RequestId string
IpAddress string
Path string
siteURLHeader string
2017-01-30 08:30:02 -05:00
}
2017-09-22 12:54:27 -05:00
func ( api * API ) ApiHandler ( h func ( * Context , http . ResponseWriter , * http . Request ) ) http . Handler {
2017-01-30 08:30:02 -05:00
return & handler {
2017-09-22 12:54:27 -05:00
app : api . App ,
2017-01-30 08:30:02 -05:00
handleFunc : h ,
requireSession : false ,
trustRequester : false ,
requireMfa : false ,
}
}
2017-09-22 12:54:27 -05:00
func ( api * API ) ApiSessionRequired ( h func ( * Context , http . ResponseWriter , * http . Request ) ) http . Handler {
2017-01-30 08:30:02 -05:00
return & handler {
2017-09-22 12:54:27 -05:00
app : api . App ,
2017-01-30 08:30:02 -05:00
handleFunc : h ,
requireSession : true ,
trustRequester : false ,
requireMfa : true ,
}
}
2017-09-22 12:54:27 -05:00
func ( api * API ) ApiSessionRequiredMfa ( h func ( * Context , http . ResponseWriter , * http . Request ) ) http . Handler {
2017-01-30 08:30:02 -05:00
return & handler {
2017-09-22 12:54:27 -05:00
app : api . App ,
2017-01-30 08:30:02 -05:00
handleFunc : h ,
requireSession : true ,
trustRequester : false ,
requireMfa : false ,
}
}
2017-09-22 12:54:27 -05:00
func ( api * API ) ApiHandlerTrustRequester ( h func ( * Context , http . ResponseWriter , * http . Request ) ) http . Handler {
2017-01-30 08:30:02 -05:00
return & handler {
2017-09-22 12:54:27 -05:00
app : api . App ,
2017-01-30 08:30:02 -05:00
handleFunc : h ,
requireSession : false ,
trustRequester : true ,
requireMfa : false ,
}
}
2017-09-22 12:54:27 -05:00
func ( api * API ) ApiSessionRequiredTrustRequester ( h func ( * Context , http . ResponseWriter , * http . Request ) ) http . Handler {
2017-01-30 08:30:02 -05:00
return & handler {
2017-09-22 12:54:27 -05:00
app : api . App ,
2017-01-30 08:30:02 -05:00
handleFunc : h ,
requireSession : true ,
trustRequester : true ,
requireMfa : true ,
}
}
type handler struct {
2017-09-22 12:54:27 -05:00
app * app . App
2017-01-30 08:30:02 -05:00
handleFunc func ( * Context , http . ResponseWriter , * http . Request )
requireSession bool
trustRequester bool
requireMfa bool
}
func ( h handler ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
now := time . Now ( )
l4g . Debug ( "%v - %v" , r . Method , r . URL . Path )
c := & Context { }
2017-09-22 12:54:27 -05:00
c . App = h . app
2017-01-30 08:30:02 -05:00
c . T , _ = utils . GetTranslationsAndLocale ( w , r )
c . RequestId = model . NewId ( )
c . IpAddress = utils . GetIpAddress ( r )
c . Params = ApiParamsFromRequest ( r )
2018-01-31 09:49:15 -08:00
token , tokenLocation := app . ParseAuthTokenFromRequest ( r )
2017-01-30 08:30:02 -05:00
2018-01-31 09:49:15 -08:00
// CSRF Check
if tokenLocation == app . TokenLocationCookie && h . requireSession && ! h . trustRequester {
if r . Header . Get ( model . HEADER_REQUESTED_WITH ) != model . HEADER_REQUESTED_WITH_XML {
c . Err = model . NewAppError ( "ServeHTTP" , "api.context.session_expired.app_error" , nil , "token=" + token + " Appears to be a CSRF attempt" , http . StatusUnauthorized )
token = ""
2017-01-30 08:30:02 -05:00
}
}
2017-04-04 11:54:52 -04:00
c . SetSiteURLHeader ( app . GetProtocol ( r ) + "://" + r . Host )
2017-01-30 08:30:02 -05:00
w . Header ( ) . Set ( model . HEADER_REQUEST_ID , c . RequestId )
2018-02-06 17:25:49 -06:00
w . Header ( ) . Set ( model . HEADER_VERSION_ID , fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , model . BuildNumber , c . App . ClientConfigHash ( ) , c . App . License ( ) != nil ) )
2017-01-30 08:30:02 -05:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
if r . Method == "GET" {
w . Header ( ) . Set ( "Expires" , "0" )
}
if len ( token ) != 0 {
2017-09-22 12:54:27 -05:00
session , err := c . App . GetSession ( token )
2017-01-30 08:30:02 -05:00
if err != nil {
2017-11-23 09:44:56 -05:00
l4g . Info ( utils . T ( "api.context.invalid_session.error" ) , err . Error ( ) )
2018-04-02 08:22:25 -07:00
if err . StatusCode == http . StatusInternalServerError {
c . Err = err
} else if h . requireSession {
c . RemoveSessionCookie ( w , r )
2017-08-31 15:03:16 +01:00
c . Err = model . NewAppError ( "ServeHTTP" , "api.context.session_expired.app_error" , nil , "token=" + token , http . StatusUnauthorized )
2017-01-30 08:30:02 -05:00
}
2018-01-31 09:49:15 -08:00
} else if ! session . IsOAuth && tokenLocation == app . TokenLocationQueryString {
2017-08-31 15:03:16 +01:00
c . Err = model . NewAppError ( "ServeHTTP" , "api.context.token_provided.app_error" , nil , "token=" + token , http . StatusUnauthorized )
2017-01-30 08:30:02 -05:00
} else {
c . Session = * session
}
2018-01-31 09:49:15 -08:00
// Rate limit by UserID
if c . App . Srv . RateLimiter != nil && c . App . Srv . RateLimiter . UserIdRateLimit ( c . Session . UserId , w ) {
return
}
2017-01-30 08:30:02 -05:00
}
c . Path = r . URL . Path
if c . Err == nil && h . requireSession {
c . SessionRequired ( )
}
if c . Err == nil && h . requireMfa {
c . MfaRequired ( )
}
if c . Err == nil {
h . handleFunc ( c , w , r )
}
2018-03-29 16:04:54 +02:00
// Handle errors that have occurred
2017-01-30 08:30:02 -05:00
if c . Err != nil {
c . Err . Translate ( c . T )
c . Err . RequestId = c . RequestId
2017-11-23 09:44:56 -05:00
if c . Err . Id == "api.context.session_expired.app_error" {
c . LogInfo ( c . Err )
} else {
c . LogError ( c . Err )
}
2017-01-30 08:30:02 -05:00
c . Err . Where = r . URL . Path
// Block out detailed error when not in developer mode
2017-10-18 15:36:43 -07:00
if ! * c . App . Config ( ) . ServiceSettings . EnableDeveloper {
2017-01-30 08:30:02 -05:00
c . Err . DetailedError = ""
}
w . WriteHeader ( c . Err . StatusCode )
w . Write ( [ ] byte ( c . Err . ToJson ( ) ) )
2017-09-19 18:31:35 -05:00
if c . App . Metrics != nil {
c . App . Metrics . IncrementHttpError ( )
2017-01-30 08:30:02 -05:00
}
}
2017-09-19 18:31:35 -05:00
if c . App . Metrics != nil {
c . App . Metrics . IncrementHttpRequest ( )
2017-01-30 08:30:02 -05:00
2017-06-26 21:46:19 -07:00
if r . URL . Path != model . API_URL_SUFFIX + "/websocket" {
2017-01-30 08:30:02 -05:00
elapsed := float64 ( time . Since ( now ) ) / float64 ( time . Second )
2017-09-19 18:31:35 -05:00
c . App . Metrics . ObserveHttpRequestDuration ( elapsed )
2017-01-30 08:30:02 -05:00
}
}
}
func ( c * Context ) LogAudit ( extraInfo string ) {
audit := & model . Audit { UserId : c . Session . UserId , IpAddress : c . IpAddress , Action : c . Path , ExtraInfo : extraInfo , SessionId : c . Session . Id }
2017-09-22 12:54:27 -05:00
if r := <- c . App . Srv . Store . Audit ( ) . Save ( audit ) ; r . Err != nil {
2017-01-30 08:30:02 -05:00
c . LogError ( r . Err )
}
}
func ( c * Context ) LogAuditWithUserId ( userId , extraInfo string ) {
if len ( c . Session . UserId ) > 0 {
extraInfo = strings . TrimSpace ( extraInfo + " session_user=" + c . Session . UserId )
}
audit := & model . Audit { UserId : userId , IpAddress : c . IpAddress , Action : c . Path , ExtraInfo : extraInfo , SessionId : c . Session . Id }
2017-09-22 12:54:27 -05:00
if r := <- c . App . Srv . Store . Audit ( ) . Save ( audit ) ; r . Err != nil {
2017-01-30 08:30:02 -05:00
c . LogError ( r . Err )
}
}
func ( c * Context ) LogError ( err * model . AppError ) {
2018-02-21 09:20:10 -05:00
// Filter out 404s, endless reconnects and browser compatibility errors
if err . StatusCode == http . StatusNotFound ||
( c . Path == "/api/v3/users/websocket" && err . StatusCode == 401 ) ||
err . Id == "web.check_browser_compatibility.app_error" {
2017-01-30 08:30:02 -05:00
c . LogDebug ( err )
} else {
2017-04-28 07:03:52 -07:00
l4g . Error ( utils . TDefault ( "api.context.log.error" ) , c . Path , err . Where , err . StatusCode ,
c . RequestId , c . Session . UserId , c . IpAddress , err . SystemMessage ( utils . TDefault ) , err . DetailedError )
2017-01-30 08:30:02 -05:00
}
}
2017-11-23 09:44:56 -05:00
func ( c * Context ) LogInfo ( err * model . AppError ) {
l4g . Info ( utils . TDefault ( "api.context.log.error" ) , c . Path , err . Where , err . StatusCode ,
c . RequestId , c . Session . UserId , c . IpAddress , err . SystemMessage ( utils . TDefault ) , err . DetailedError )
}
2017-01-30 08:30:02 -05:00
func ( c * Context ) LogDebug ( err * model . AppError ) {
2017-04-28 07:03:52 -07:00
l4g . Debug ( utils . TDefault ( "api.context.log.error" ) , c . Path , err . Where , err . StatusCode ,
c . RequestId , c . Session . UserId , c . IpAddress , err . SystemMessage ( utils . TDefault ) , err . DetailedError )
2017-01-30 08:30:02 -05:00
}
func ( c * Context ) IsSystemAdmin ( ) bool {
2017-10-25 11:48:15 -07:00
return c . App . SessionHasPermissionTo ( c . Session , model . PERMISSION_MANAGE_SYSTEM )
2017-01-30 08:30:02 -05:00
}
func ( c * Context ) SessionRequired ( ) {
2017-10-18 15:36:43 -07:00
if ! * c . App . Config ( ) . ServiceSettings . EnableUserAccessTokens && c . Session . Props [ model . SESSION_PROP_TYPE ] == model . SESSION_TYPE_USER_ACCESS_TOKEN {
2017-07-31 12:59:32 -04:00
c . Err = model . NewAppError ( "" , "api.context.session_expired.app_error" , nil , "UserAccessToken" , http . StatusUnauthorized )
return
}
2017-01-30 08:30:02 -05:00
if len ( c . Session . UserId ) == 0 {
2017-04-10 08:19:49 -04:00
c . Err = model . NewAppError ( "" , "api.context.session_expired.app_error" , nil , "UserRequired" , http . StatusUnauthorized )
2017-01-30 08:30:02 -05:00
return
}
}
func ( c * Context ) MfaRequired ( ) {
// Must be licensed for MFA and have it configured for enforcement
2018-02-06 17:25:49 -06:00
if license := c . App . License ( ) ; license == nil || ! * license . Features . MFA || ! * c . App . Config ( ) . ServiceSettings . EnableMultifactorAuthentication || ! * c . App . Config ( ) . ServiceSettings . EnforceMultifactorAuthentication {
2017-01-30 08:30:02 -05:00
return
}
// OAuth integrations are excepted
if c . Session . IsOAuth {
return
}
2017-09-22 12:54:27 -05:00
if user , err := c . App . GetUser ( c . Session . UserId ) ; err != nil {
2017-08-31 15:03:16 +01:00
c . Err = model . NewAppError ( "" , "api.context.session_expired.app_error" , nil , "MfaRequired" , http . StatusUnauthorized )
2017-01-30 08:30:02 -05:00
return
} else {
// Only required for email and ldap accounts
if user . AuthService != "" &&
user . AuthService != model . USER_AUTH_SERVICE_EMAIL &&
user . AuthService != model . USER_AUTH_SERVICE_LDAP {
return
}
2017-05-09 07:48:57 -05:00
// Special case to let user get themself
if c . Path == "/api/v4/users/me" {
return
}
2017-01-30 08:30:02 -05:00
if ! user . MfaActive {
2017-05-09 07:48:57 -05:00
c . Err = model . NewAppError ( "" , "api.context.mfa_required.app_error" , nil , "MfaRequired" , http . StatusForbidden )
2017-01-30 08:30:02 -05:00
return
}
}
}
func ( c * Context ) RemoveSessionCookie ( w http . ResponseWriter , r * http . Request ) {
cookie := & http . Cookie {
Name : model . SESSION_COOKIE_TOKEN ,
Value : "" ,
Path : "/" ,
MaxAge : - 1 ,
HttpOnly : true ,
}
http . SetCookie ( w , cookie )
}
func ( c * Context ) SetInvalidParam ( parameter string ) {
c . Err = NewInvalidParamError ( parameter )
}
func ( c * Context ) SetInvalidUrlParam ( parameter string ) {
c . Err = NewInvalidUrlParamError ( parameter )
}
2017-09-22 12:54:27 -05:00
func ( c * Context ) HandleEtag ( etag string , routeName string , w http . ResponseWriter , r * http . Request ) bool {
metrics := c . App . Metrics
if et := r . Header . Get ( model . HEADER_ETAG_CLIENT ) ; len ( etag ) > 0 {
if et == etag {
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
w . WriteHeader ( http . StatusNotModified )
if metrics != nil {
metrics . IncrementEtagHitCounter ( routeName )
}
return true
}
}
if metrics != nil {
metrics . IncrementEtagMissCounter ( routeName )
}
return false
}
2017-01-30 08:30:02 -05:00
func NewInvalidParamError ( parameter string ) * model . AppError {
2017-08-31 15:03:16 +01:00
err := model . NewAppError ( "Context" , "api.context.invalid_body_param.app_error" , map [ string ] interface { } { "Name" : parameter } , "" , http . StatusBadRequest )
2017-01-30 08:30:02 -05:00
return err
}
func NewInvalidUrlParamError ( parameter string ) * model . AppError {
2017-08-31 15:03:16 +01:00
err := model . NewAppError ( "Context" , "api.context.invalid_url_param.app_error" , map [ string ] interface { } { "Name" : parameter } , "" , http . StatusBadRequest )
2017-01-30 08:30:02 -05:00
return err
}
func ( c * Context ) SetPermissionError ( permission * model . Permission ) {
2017-08-31 15:03:16 +01:00
c . Err = model . NewAppError ( "Permissions" , "api.context.permissions.app_error" , nil , "userId=" + c . Session . UserId + ", " + "permission=" + permission . Id , http . StatusForbidden )
2017-01-30 08:30:02 -05:00
}
2017-04-04 11:54:52 -04:00
func ( c * Context ) SetSiteURLHeader ( url string ) {
c . siteURLHeader = strings . TrimRight ( url , "/" )
2017-01-30 08:30:02 -05:00
}
2017-04-04 11:54:52 -04:00
func ( c * Context ) GetSiteURLHeader ( ) string {
return c . siteURLHeader
2017-01-30 08:30:02 -05:00
}
func ( c * Context ) RequireUserId ( ) * Context {
if c . Err != nil {
return c
}
2017-03-21 18:43:16 -04:00
if c . Params . UserId == model . ME {
c . Params . UserId = c . Session . UserId
}
2017-01-30 08:30:02 -05:00
if len ( c . Params . UserId ) != 26 {
c . SetInvalidUrlParam ( "user_id" )
}
return c
}
func ( c * Context ) RequireTeamId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . TeamId ) != 26 {
c . SetInvalidUrlParam ( "team_id" )
}
return c
}
2017-06-20 09:55:43 -04:00
func ( c * Context ) RequireInviteId ( ) * Context {
if c . Err != nil {
return c
}
2017-07-14 17:06:59 -04:00
if len ( c . Params . InviteId ) == 0 {
2017-06-20 09:55:43 -04:00
c . SetInvalidUrlParam ( "invite_id" )
}
return c
}
2017-07-31 12:59:32 -04:00
func ( c * Context ) RequireTokenId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . TokenId ) != 26 {
c . SetInvalidUrlParam ( "token_id" )
}
return c
}
2017-01-30 08:30:02 -05:00
func ( c * Context ) RequireChannelId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . ChannelId ) != 26 {
c . SetInvalidUrlParam ( "channel_id" )
}
return c
}
2017-02-07 11:54:07 -05:00
2017-02-08 05:00:16 -05:00
func ( c * Context ) RequireUsername ( ) * Context {
if c . Err != nil {
return c
}
2017-02-14 10:28:08 -05:00
if ! model . IsValidUsername ( c . Params . Username ) {
c . SetInvalidParam ( "username" )
2017-02-08 05:00:16 -05:00
}
return c
}
2017-02-13 10:52:50 -05:00
func ( c * Context ) RequirePostId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . PostId ) != 26 {
c . SetInvalidUrlParam ( "post_id" )
}
return c
}
2017-04-20 09:55:02 -04:00
func ( c * Context ) RequireAppId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . AppId ) != 26 {
c . SetInvalidUrlParam ( "app_id" )
}
return c
}
2017-02-17 10:31:21 -05:00
func ( c * Context ) RequireFileId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . FileId ) != 26 {
c . SetInvalidUrlParam ( "file_id" )
}
return c
}
2018-02-20 10:41:00 -05:00
func ( c * Context ) RequireFilename ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . Filename ) == 0 {
c . SetInvalidUrlParam ( "filename" )
}
return c
}
2017-09-01 09:00:27 -04:00
func ( c * Context ) RequirePluginId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . PluginId ) == 0 {
c . SetInvalidUrlParam ( "plugin_id" )
}
return c
}
2017-03-13 10:14:16 -04:00
func ( c * Context ) RequireReportId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . ReportId ) != 26 {
c . SetInvalidUrlParam ( "report_id" )
}
return c
}
2017-04-17 16:07:28 +02:00
func ( c * Context ) RequireEmojiId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . EmojiId ) != 26 {
c . SetInvalidUrlParam ( "emoji_id" )
}
return c
}
2017-02-14 10:28:08 -05:00
func ( c * Context ) RequireTeamName ( ) * Context {
if c . Err != nil {
return c
}
2017-02-17 10:31:21 -05:00
if ! model . IsValidTeamName ( c . Params . TeamName ) {
2017-02-14 10:28:08 -05:00
c . SetInvalidUrlParam ( "team_name" )
}
return c
}
func ( c * Context ) RequireChannelName ( ) * Context {
if c . Err != nil {
return c
}
if ! model . IsValidChannelIdentifier ( c . Params . ChannelName ) {
c . SetInvalidUrlParam ( "channel_name" )
2017-02-17 10:31:21 -05:00
}
2017-02-14 10:28:08 -05:00
return c
}
2017-02-07 11:54:07 -05:00
func ( c * Context ) RequireEmail ( ) * Context {
if c . Err != nil {
return c
}
2017-02-14 10:28:08 -05:00
if ! model . IsValidEmail ( c . Params . Email ) {
2017-02-07 11:54:07 -05:00
c . SetInvalidUrlParam ( "email" )
}
return c
}
2017-02-28 04:14:16 -05:00
func ( c * Context ) RequireCategory ( ) * Context {
if c . Err != nil {
return c
}
2017-04-22 21:52:03 +09:00
if ! model . IsValidAlphaNumHyphenUnderscore ( c . Params . Category , true ) {
2017-02-28 04:14:16 -05:00
c . SetInvalidUrlParam ( "category" )
}
return c
}
2017-04-20 09:55:02 -04:00
func ( c * Context ) RequireService ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . Service ) == 0 {
c . SetInvalidUrlParam ( "service" )
}
return c
}
2017-02-28 04:14:16 -05:00
func ( c * Context ) RequirePreferenceName ( ) * Context {
if c . Err != nil {
return c
}
2017-04-22 21:52:03 +09:00
if ! model . IsValidAlphaNumHyphenUnderscore ( c . Params . PreferenceName , true ) {
2017-02-28 04:14:16 -05:00
c . SetInvalidUrlParam ( "preference_name" )
}
return c
}
2017-03-12 04:10:56 +05:30
2017-04-22 21:52:03 +09:00
func ( c * Context ) RequireEmojiName ( ) * Context {
if c . Err != nil {
return c
}
2017-04-26 23:11:32 +09:00
validName := regexp . MustCompile ( ` ^[a-zA-Z0-9\-\+_]+$ ` )
2018-01-11 16:57:47 +01:00
if len ( c . Params . EmojiName ) == 0 || len ( c . Params . EmojiName ) > model . EMOJI_NAME_MAX_LENGTH || ! validName . MatchString ( c . Params . EmojiName ) {
2017-04-22 21:52:03 +09:00
c . SetInvalidUrlParam ( "emoji_name" )
}
return c
}
2017-03-12 04:10:56 +05:30
func ( c * Context ) RequireHookId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . HookId ) != 26 {
c . SetInvalidUrlParam ( "hook_id" )
}
return c
}
2017-04-08 02:06:09 +09:00
func ( c * Context ) RequireCommandId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . CommandId ) != 26 {
c . SetInvalidUrlParam ( "command_id" )
}
return c
}
2017-05-18 15:05:57 -04:00
func ( c * Context ) RequireJobId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . JobId ) != 26 {
c . SetInvalidUrlParam ( "job_id" )
}
return c
}
func ( c * Context ) RequireJobType ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . JobType ) == 0 || len ( c . Params . JobType ) > 32 {
c . SetInvalidUrlParam ( "job_type" )
}
return c
}
2017-08-29 16:14:59 -05:00
func ( c * Context ) RequireActionId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . ActionId ) != 26 {
c . SetInvalidUrlParam ( "action_id" )
}
return c
}
2018-02-06 15:34:08 +00:00
func ( c * Context ) RequireRoleId ( ) * Context {
if c . Err != nil {
return c
}
if len ( c . Params . RoleId ) != 26 {
c . SetInvalidUrlParam ( "role_id" )
}
return c
}
func ( c * Context ) RequireRoleName ( ) * Context {
if c . Err != nil {
return c
}
if ! model . IsValidRoleName ( c . Params . RoleName ) {
c . SetInvalidUrlParam ( "role_name" )
}
return c
}