package handler
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"net/http"
"net/http/httputil"
"strings"
"text/template"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/sessdata"
)
var (
profileHash = ""
certHash = ""
)
func LinkAuth(w http.ResponseWriter, r *http.Request) {
// TODO 调试信息输出
if base.GetLogLevel() == base.LogLevelTrace {
hd, _ := httputil.DumpRequest(r, true)
base.Trace("LinkAuth: ", string(hd))
}
// 判断anyconnect客户端
userAgent := strings.ToLower(r.UserAgent())
xAggregateAuth := r.Header.Get("X-Aggregate-Auth")
xTranscendVersion := r.Header.Get("X-Transcend-Version")
if !((strings.Contains(userAgent, "anyconnect") || strings.Contains(userAgent, "openconnect") || strings.Contains(userAgent, "anylink")) &&
xAggregateAuth == "1" && xTranscendVersion == "1") {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "error request")
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
defer r.Body.Close()
cr := &ClientRequest{
RemoteAddr: r.RemoteAddr,
UserAgent: userAgent,
}
err = xml.Unmarshal(body, &cr)
if err != nil {
base.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
}
base.Trace(fmt.Sprintf("%+v \n", cr))
// setCommonHeader(w)
if cr.Type == "logout" {
// 退出删除session信息
if cr.SessionToken != "" {
sessdata.DelSessByStoken(cr.SessionToken)
}
w.WriteHeader(http.StatusOK)
return
}
if cr.Type == "init" {
w.WriteHeader(http.StatusOK)
data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNamesNormal()}
tplRequest(tpl_request, w, data)
return
}
// 登陆参数判断
if cr.Type != "auth-reply" {
w.WriteHeader(http.StatusBadRequest)
return
}
// 用户活动日志
ua := &dbdata.UserActLog{
Username: cr.Auth.Username,
GroupName: cr.GroupSelect,
RemoteAddr: r.RemoteAddr,
Status: dbdata.UserAuthSuccess,
DeviceType: cr.DeviceId.DeviceType,
PlatformVersion: cr.DeviceId.PlatformVersion,
}
sessionData := &AuthSession{
ClientRequest: cr,
UserActLog: ua,
}
// TODO 用户密码校验
err = dbdata.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect)
if err != nil {
lockManager.LoginStatus.Store(loginStatusKey, false) // 记录登录失败状态
base.Warn(err, r.RemoteAddr)
ua.Info = err.Error()
ua.Status = dbdata.UserAuthFail
dbdata.UserActLogIns.Add(*ua, userAgent)
w.WriteHeader(http.StatusOK)
data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNamesNormal(), Error: "用户名或密码错误"}
if base.Cfg.DisplayError {
data.Error = err.Error()
}
tplRequest(tpl_request, w, data)
return
}
dbdata.UserActLogIns.Add(*ua, userAgent)
v := &dbdata.User{}
err = dbdata.One("Username", cr.Auth.Username, v)
if err != nil {
base.Info("正在使用第三方认证方式登录")
CreateSession(w, r, sessionData)
return
}
// 用户otp验证
if !v.DisableOtp {
lockManager.LoginStatus.Store(loginStatusKey, true) // 重置OTP验证计数
sessionID, err := GenerateSessionID()
if err != nil {
base.Error("Failed to generate session ID: ", err)
http.Error(w, "Failed to generate session ID", http.StatusInternalServerError)
return
}
sessionData.ClientRequest.Auth.OtpSecret = v.OtpSecret
SessStore.SaveAuthSession(sessionID, sessionData)
SetCookie(w, "auth-session-id", sessionID, 0)
data := RequestData{}
w.WriteHeader(http.StatusOK)
tplRequest(tpl_otp, w, data)
return
}
CreateSession(w, r, sessionData)
}
const (
tpl_request = iota
tpl_complete
tpl_otp
)
func tplRequest(typ int, w io.Writer, data RequestData) {
switch typ {
case tpl_request:
t, _ := template.New("auth_request").Parse(auth_request)
_ = t.Execute(w, data)
case tpl_complete:
if data.Banner != "" {
buf := new(bytes.Buffer)
_ = xml.EscapeText(buf, []byte(data.Banner))
data.Banner = buf.String()
}
t, _ := template.New("auth_complete").Parse(auth_complete)
_ = t.Execute(w, data)
case tpl_otp:
t, _ := template.New("auth_otp").Parse(auth_otp)
_ = t.Execute(w, data)
}
}
// 设置输出信息
type RequestData struct {
Groups []string
Group string
Error string
// complete
SessionId string
SessionToken string
Banner string
ProfileName string
ProfileHash string
CertHash string
}
var auth_request = `
{{.Group}}
{{.Group}}
168179266
1595829378234
multiple-cert
single-sign-on-v2
Login
请输入你的用户名和密码
{{if .Error}}
登陆失败: %s
{{end}}
`
var auth_complete = `
{{.SessionId}}
{{.SessionToken}}
{{.Banner}}
ssl-dhe
{{.CertHash}}
/profile_{{.ProfileName}}.xml
{{.ProfileHash}}
`
// var auth_profile = `
//
//
// false
// false
// false
// IPSec
// true
// AllowRemoteUsers
// pinAllowed
//
//
// Digital_Signature
//
//
// ClientAuth
//
//
//
// localhost
//
//
//
//
// VPN Server
// localhost
//
//
//
//
// `
var ds_domains_xml = `
{{if .DsExcludeDomains}}
{{else if .DsIncludeDomains}}
{{end}}
`