HEX
Server: Apache/2.4.54 (Win64) OpenSSL/1.1.1p PHP/7.4.30
System: Windows NT website-api 10.0 build 20348 (Windows Server 2016) AMD64
User: SYSTEM (0)
PHP: 7.4.30
Disabled: NONE
Upload Files
File: C:/github_repos/caswaf_my/service/captcha.go
// Copyright 2023 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"time"

	"github.com/casbin/caswaf/conf"
	"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
	"github.com/google/uuid"
)

type verifyResponse struct {
	Status string      `json:"status"`
	Msg    string      `json:"msg"`
	Sub    string      `json:"sub"`
	Name   string      `json:"name"`
	Data   bool        `json:"data"`
	Data2  interface{} `json:"data2"`
}

var verifiedSession = make(map[string]time.Time)

func redirectToCaptcha(w http.ResponseWriter, r *http.Request) {
	scheme := getScheme(r)
	callbackUrl := fmt.Sprintf("%s://%s/caswaf-captcha-verify", scheme, r.Host)
	captchaUri := fmt.Sprintf(
		"%s/captcha?client_id=%s&redirect_uri=%s&state=%s",
		getCasdoorEndpoint(),
		conf.GetConfigString("clientId"),
		callbackUrl,
		conf.GetConfigString("casdoorApplication"),
	)
	http.Redirect(w, r, captchaUri, http.StatusFound)
}

func handleCaptchaCallback(w http.ResponseWriter, r *http.Request) {
	host := r.Host

	code := r.URL.Query().Get("code")
	typeStr := r.URL.Query().Get("type")
	secret := r.URL.Query().Get("secret")
	applicationId := r.URL.Query().Get("applicationId")
	if code == "" || typeStr == "" || secret == "" || applicationId == "" {
		redirectToCaptcha(w, r)
	}

	var b bytes.Buffer
	writer := multipart.NewWriter(&b)
	_ = writer.WriteField("captchaToken", code)
	_ = writer.WriteField("clientSecret", secret)
	_ = writer.WriteField("applicationId", applicationId)
	_ = writer.WriteField("captchaType", typeStr)
	_ = writer.Close()
	verifyURL := casdoorsdk.GetUrl("verify-captcha", nil)
	req, err := http.NewRequest("POST", verifyURL, &b)
	if err != nil {
		redirectToCaptcha(w, r)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		redirectToCaptcha(w, r)
	}
	// read response
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		redirectToCaptcha(w, r)
	}
	// parse response
	var vr verifyResponse
	err = json.Unmarshal(body, &vr)
	if err != nil {
		redirectToCaptcha(w, r)
	}
	if vr.Status != "ok" || !vr.Data {
		redirectToCaptcha(w, r)
	}
	// set verified session
	uuidStr := uuid.NewString()
	verifiedSession[uuidStr] = time.Now()
	cookie := &http.Cookie{
		Name:       "casdoor_captcha_token",
		Value:      uuidStr,
		Path:       "/",
		Domain:     host,
		Expires:    time.Now().Add(30 * time.Minute),
		RawExpires: "",
		MaxAge:     0,
		Secure:     false,
		HttpOnly:   false,
		SameSite:   0,
		Raw:        "",
		Unparsed:   nil,
	}

	scheme := getScheme(r)
	http.SetCookie(w, cookie)
	http.Redirect(w, r, scheme+"://"+host, http.StatusFound)
	return
}

func isVerifiedSession(r *http.Request) bool {
	cookie, err := r.Cookie("casdoor_captcha_token")
	if err != nil {
		return false
	}
	token := cookie.Value
	if token == "" {
		return false
	}
	t, ok := verifiedSession[token]
	if ok {
		if time.Now().Sub(t) < 30*time.Minute {
			return true
		}
		delete(verifiedSession, token)
	}
	return false
}