github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/web/saml.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package web 5 6 import ( 7 b64 "encoding/base64" 8 "net/http" 9 "strings" 10 11 "github.com/vnforks/kid/v5/audit" 12 "github.com/vnforks/kid/v5/mlog" 13 "github.com/vnforks/kid/v5/model" 14 ) 15 16 func (w *Web) InitSaml() { 17 w.MainRouter.Handle("/login/sso/saml", w.ApiHandler(loginWithSaml)).Methods("GET") 18 w.MainRouter.Handle("/login/sso/saml", w.ApiHandlerTrustRequester(completeSaml)).Methods("POST") 19 } 20 21 func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) { 22 samlInterface := c.App.Saml() 23 24 if samlInterface == nil { 25 c.Err = model.NewAppError("loginWithSaml", "api.user.saml.not_available.app_error", nil, "", http.StatusFound) 26 return 27 } 28 29 branchId, err := c.App.GetBranchIdFromQuery(r.URL.Query()) 30 if err != nil { 31 c.Err = err 32 return 33 } 34 action := r.URL.Query().Get("action") 35 redirectTo := r.URL.Query().Get("redirect_to") 36 relayProps := map[string]string{} 37 relayState := "" 38 39 if len(action) != 0 { 40 relayProps["branch_id"] = branchId 41 relayProps["action"] = action 42 if action == model.OAUTH_ACTION_EMAIL_TO_SSO { 43 relayProps["email"] = r.URL.Query().Get("email") 44 } 45 } 46 47 if len(redirectTo) != 0 { 48 relayProps["redirect_to"] = redirectTo 49 } 50 51 if len(relayProps) > 0 { 52 relayState = b64.StdEncoding.EncodeToString([]byte(model.MapToJson(relayProps))) 53 } 54 55 if data, err := samlInterface.BuildRequest(relayState); err != nil { 56 c.Err = err 57 return 58 } else { 59 w.Header().Set("Content-Type", "application/x-www-form-urlencoded") 60 http.Redirect(w, r, data.URL, http.StatusFound) 61 } 62 } 63 64 func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) { 65 samlInterface := c.App.Saml() 66 67 if samlInterface == nil { 68 c.Err = model.NewAppError("completeSaml", "api.user.saml.not_available.app_error", nil, "", http.StatusFound) 69 return 70 } 71 72 //Validate that the user is with SAML and all that 73 encodedXML := r.FormValue("SAMLResponse") 74 relayState := r.FormValue("RelayState") 75 76 relayProps := make(map[string]string) 77 if len(relayState) > 0 { 78 stateStr := "" 79 if b, err := b64.StdEncoding.DecodeString(relayState); err != nil { 80 c.Err = model.NewAppError("completeSaml", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error(), http.StatusFound) 81 return 82 } else { 83 stateStr = string(b) 84 } 85 relayProps = model.MapFromJson(strings.NewReader(stateStr)) 86 } 87 88 auditRec := c.MakeAuditRecord("completeSaml", audit.Fail) 89 defer c.LogAuditRec(auditRec) 90 c.LogAudit("attempt") 91 92 action := relayProps["action"] 93 auditRec.AddMeta("action", action) 94 95 user, err := samlInterface.DoLogin(encodedXML, relayProps) 96 if err != nil { 97 c.LogAudit("fail") 98 if action == model.OAUTH_ACTION_MOBILE { 99 err.Translate(c.App.T) 100 w.Write([]byte(err.ToJson())) 101 } else { 102 c.Err = err 103 c.Err.StatusCode = http.StatusFound 104 } 105 return 106 } 107 108 if err = c.App.CheckUserAllAuthenticationCriteria(user, ""); err != nil { 109 c.Err = err 110 c.Err.StatusCode = http.StatusFound 111 return 112 } 113 114 switch action { 115 case model.OAUTH_ACTION_SIGNUP: 116 branchId := relayProps["branch_id"] 117 if len(branchId) > 0 { 118 c.App.Srv().Go(func() { 119 if err = c.App.AddUserToBranchByBranchId(branchId, user); err != nil { 120 mlog.Error(err.Error()) 121 } else { 122 c.App.AddDirectClasses(branchId, user) 123 } 124 }) 125 } 126 case model.OAUTH_ACTION_EMAIL_TO_SSO: 127 if err = c.App.RevokeAllSessions(user.Id); err != nil { 128 c.Err = err 129 return 130 } 131 auditRec.AddMeta("revoked_user_id", user.Id) 132 auditRec.AddMeta("revoked", "Revoked all sessions for user") 133 134 c.LogAuditWithUserId(user.Id, "Revoked all sessions for user") 135 c.App.Srv().Go(func() { 136 if err = c.App.SendSignInChangeEmail(user.Email, strings.Title(model.USER_AUTH_SERVICE_SAML)+" SSO", user.Locale, c.App.GetSiteURL()); err != nil { 137 mlog.Error(err.Error()) 138 } 139 }) 140 } 141 142 auditRec.AddMeta("obtained_user_id", user.Id) 143 c.LogAuditWithUserId(user.Id, "obtained user") 144 145 err = c.App.DoLogin(w, r, user, "") 146 if err != nil { 147 c.Err = err 148 return 149 } 150 151 auditRec.Success() 152 c.LogAuditWithUserId(user.Id, "success") 153 154 c.App.AttachSessionCookies(w, r) 155 156 if val, ok := relayProps["redirect_to"]; ok { 157 http.Redirect(w, r, c.GetSiteURLHeader()+val, http.StatusFound) 158 return 159 } 160 161 switch action { 162 case model.OAUTH_ACTION_MOBILE: 163 ReturnStatusOK(w) 164 case model.OAUTH_ACTION_EMAIL_TO_SSO: 165 http.Redirect(w, r, c.GetSiteURLHeader()+"/login?extra=signin_change", http.StatusFound) 166 default: 167 http.Redirect(w, r, c.GetSiteURLHeader(), http.StatusFound) 168 } 169 }