mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-29 18:50:35 +08:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aafdc546fa | ||
![]() |
539ca2d731 | ||
![]() |
ea326b3513 | ||
![]() |
98ef766fb4 | ||
![]() |
e94ada9ea2 | ||
![]() |
4ea482223d | ||
![]() |
d55ae7d1d2 | ||
![]() |
d72e00605f |
82
captcha/geetest.go
Normal file
82
captcha/geetest.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2022 The Casdoor 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 captcha
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
const GEETESTCaptchaVerifyUrl = "http://gcaptcha4.geetest.com/validate"
|
||||
|
||||
type GEETESTCaptchaProvider struct {
|
||||
}
|
||||
|
||||
func NewGEETESTCaptchaProvider() *GEETESTCaptchaProvider {
|
||||
captcha := &GEETESTCaptchaProvider{}
|
||||
return captcha
|
||||
}
|
||||
|
||||
func (captcha *GEETESTCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
|
||||
pathData, err := url.ParseQuery(token)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
signToken := util.GetHmacSha256(clientSecret, pathData["lot_number"][0])
|
||||
|
||||
formData := make(url.Values)
|
||||
formData["lot_number"] = []string{pathData["lot_number"][0]}
|
||||
formData["captcha_output"] = []string{pathData["captcha_output"][0]}
|
||||
formData["pass_token"] = []string{pathData["pass_token"][0]}
|
||||
formData["gen_time"] = []string{pathData["gen_time"][0]}
|
||||
formData["sign_token"] = []string{signToken}
|
||||
captchaId := pathData["captcha_id"][0]
|
||||
|
||||
cli := http.Client{Timeout: time.Second * 5}
|
||||
resp, err := cli.PostForm(fmt.Sprintf("%s?captcha_id=%s", GEETESTCaptchaVerifyUrl, captchaId), formData)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
type captchaResponse struct {
|
||||
Result string `json:"result"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
captchaResp := &captchaResponse{}
|
||||
err = json.Unmarshal(body, captchaResp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if captchaResp.Result == "success" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, errors.New(captchaResp.Reason)
|
||||
}
|
@@ -27,6 +27,8 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
|
||||
return NewHCaptchaProvider()
|
||||
} else if captchaType == "Aliyun Captcha" {
|
||||
return NewAliyunCaptchaProvider()
|
||||
} else if captchaType == "GEETEST" {
|
||||
return NewGEETESTCaptchaProvider()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@@ -14,6 +14,7 @@ require (
|
||||
github.com/casdoor/goth v1.69.0-FIX2
|
||||
github.com/casdoor/oss v1.2.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
|
||||
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||
github.com/go-ldap/ldap/v3 v3.3.0
|
||||
@@ -23,6 +24,7 @@ require (
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/lestrrat-go/jwx v0.9.0
|
||||
github.com/lib/pq v1.8.0
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
|
2
go.sum
2
go.sum
@@ -125,6 +125,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
|
||||
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
|
||||
@@ -173,6 +174,7 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@@ -21,10 +21,10 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
//_ "github.com/denisenkom/go-mssqldb" // db = mssql
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
//_ "github.com/lib/pq" // db = postgres
|
||||
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
|
||||
_ "github.com/denisenkom/go-mssqldb" // db = mssql
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
_ "github.com/lib/pq" // db = postgres
|
||||
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
@@ -417,6 +417,10 @@ func AddUser(user *User) bool {
|
||||
}
|
||||
|
||||
organization := GetOrganizationByUser(user)
|
||||
if organization == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
user.UpdateUserPassword(organization)
|
||||
|
||||
user.UpdateUserHash()
|
||||
|
@@ -17,7 +17,9 @@ package util
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func GetHmacSha1(keyStr, value string) string {
|
||||
@@ -28,3 +30,10 @@ func GetHmacSha1(keyStr, value string) string {
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func GetHmacSha256(key string, data string) string {
|
||||
mac := hmac.New(sha256.New, []byte(key))
|
||||
mac.Write([]byte(data))
|
||||
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
@@ -39,7 +39,8 @@
|
||||
"test": "craco test",
|
||||
"eject": "craco eject",
|
||||
"crowdin:sync": "crowdin upload && crowdin download",
|
||||
"preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\""
|
||||
"preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\"",
|
||||
"fix": "eslint --fix ."
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
@@ -38,7 +38,7 @@ class AccountTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
let row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true};
|
||||
let row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true, viewRule: "Public", modifyRule: "Self"};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
|
@@ -208,6 +208,8 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("domain", Setting.getFullServerUrl());
|
||||
} else if (value === "SAML") {
|
||||
this.updateProviderField("type", "Aliyun IDaaS");
|
||||
} else if (value === "Payment") {
|
||||
this.updateProviderField("type", "Alipay");
|
||||
} else if (value === "Captcha") {
|
||||
this.updateProviderField("type", "Default");
|
||||
}
|
||||
|
@@ -122,6 +122,10 @@ export const OtherProviderInfo = {
|
||||
"Aliyun Captcha": {
|
||||
logo: `${StaticBaseUrl}/img/social_aliyun.png`,
|
||||
url: "https://help.aliyun.com/product/28308.html",
|
||||
},
|
||||
"GEETEST": {
|
||||
logo: `${StaticBaseUrl}/img/social_geetest.png`,
|
||||
url: "https://www.geetest.com",
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -615,6 +619,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "reCAPTCHA", name: "reCAPTCHA"},
|
||||
{id: "hCaptcha", name: "hCaptcha"},
|
||||
{id: "Aliyun Captcha", name: "Aliyun Captcha"},
|
||||
{id: "GEETEST", name: "GEETEST"},
|
||||
]);
|
||||
} else {
|
||||
return [];
|
||||
|
@@ -20,35 +20,13 @@ import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||
import * as Provider from "./Provider";
|
||||
import * as ProviderButton from "./ProviderButton";
|
||||
import * as Util from "./Util";
|
||||
import * as Setting from "../Setting";
|
||||
import SelfLoginButton from "./SelfLoginButton";
|
||||
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
|
||||
import FacebookLoginButton from "./FacebookLoginButton";
|
||||
import QqLoginButton from "./QqLoginButton";
|
||||
import DingTalkLoginButton from "./DingTalkLoginButton";
|
||||
import GiteeLoginButton from "./GiteeLoginButton";
|
||||
import WechatLoginButton from "./WechatLoginButton";
|
||||
import WeiboLoginButton from "./WeiboLoginButton";
|
||||
import i18next from "i18next";
|
||||
import LinkedInLoginButton from "./LinkedInLoginButton";
|
||||
import WeComLoginButton from "./WeComLoginButton";
|
||||
import LarkLoginButton from "./LarkLoginButton";
|
||||
import GitLabLoginButton from "./GitLabLoginButton";
|
||||
import AdfsLoginButton from "./AdfsLoginButton";
|
||||
import BaiduLoginButton from "./BaiduLoginButton";
|
||||
import AlipayLoginButton from "./AlipayLoginButton";
|
||||
import CasdoorLoginButton from "./CasdoorLoginButton";
|
||||
import InfoflowLoginButton from "./InfoflowLoginButton";
|
||||
import AppleLoginButton from "./AppleLoginButton";
|
||||
import AzureADLoginButton from "./AzureADLoginButton";
|
||||
import SlackLoginButton from "./SlackLoginButton";
|
||||
import SteamLoginButton from "./SteamLoginButton";
|
||||
import OktaLoginButton from "./OktaLoginButton";
|
||||
import DouyinLoginButton from "./DouyinLoginButton";
|
||||
import CustomGithubCorner from "../CustomGithubCorner";
|
||||
import {CountDownInput} from "../common/CountDownInput";
|
||||
import BilibiliLoginButton from "./BilibiliLoginButton";
|
||||
|
||||
const {TabPane} = Tabs;
|
||||
|
||||
@@ -146,6 +124,16 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
|
||||
onFinish(values) {
|
||||
if (this.state.loginMethod === "webAuthn") {
|
||||
let username = this.state.username;
|
||||
if (username === null || username === "") {
|
||||
username = values["username"];
|
||||
}
|
||||
|
||||
this.signInWithWebAuthn(username);
|
||||
return;
|
||||
}
|
||||
|
||||
const application = this.getApplicationObj();
|
||||
const ths = this;
|
||||
|
||||
@@ -260,61 +248,6 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getSigninButton(type) {
|
||||
const text = i18next.t("login:Sign in with {type}").replace("{type}", type);
|
||||
if (type === "GitHub") {
|
||||
return <GithubLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Google") {
|
||||
return <GoogleLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "QQ") {
|
||||
return <QqLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Facebook") {
|
||||
return <FacebookLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Weibo") {
|
||||
return <WeiboLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Gitee") {
|
||||
return <GiteeLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "WeChat") {
|
||||
return <WechatLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "DingTalk") {
|
||||
return <DingTalkLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "LinkedIn") {
|
||||
return <LinkedInLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "WeCom") {
|
||||
return <WeComLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Lark") {
|
||||
return <LarkLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "GitLab") {
|
||||
return <GitLabLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Adfs") {
|
||||
return <AdfsLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Casdoor") {
|
||||
return <CasdoorLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Baidu") {
|
||||
return <BaiduLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Alipay") {
|
||||
return <AlipayLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Infoflow") {
|
||||
return <InfoflowLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Apple") {
|
||||
return <AppleLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "AzureAD") {
|
||||
return <AzureADLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Slack") {
|
||||
return <SlackLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Steam") {
|
||||
return <SteamLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Bilibili") {
|
||||
return <BilibiliLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Okta") {
|
||||
return <OktaLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Douyin") {
|
||||
return <DouyinLoginButton text={text} align={"center"} />;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
getSamlUrl(provider) {
|
||||
const params = new URLSearchParams(this.props.location.search);
|
||||
let clientId = params.get("client_id");
|
||||
@@ -332,35 +265,6 @@ class LoginPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderProviderLogo(provider, application, width, margin, size) {
|
||||
if (size === "small") {
|
||||
if (provider.category === "OAuth") {
|
||||
return (
|
||||
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")}>
|
||||
<img width={width} height={width} src={Setting.getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} />
|
||||
</a>
|
||||
);
|
||||
} else if (provider.category === "SAML") {
|
||||
return (
|
||||
<a key={provider.displayName} onClick={this.getSamlUrl.bind(this, provider)}>
|
||||
<img width={width} height={width} src={Setting.getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
return (
|
||||
<div key={provider.displayName} style={{marginBottom: "10px"}}>
|
||||
<a href={Provider.getAuthUrl(application, provider, "signup")}>
|
||||
{
|
||||
this.getSigninButton(provider.type)
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isProviderVisible(providerItem) {
|
||||
if (this.state.mode === "signup") {
|
||||
return Setting.isProviderVisibleForSignUp(providerItem);
|
||||
@@ -499,9 +403,14 @@ class LoginPage extends React.Component {
|
||||
</Button>
|
||||
) :
|
||||
(
|
||||
<Button type="primary" style={{width: "100%", marginBottom: "5px"}} onClick={() => this.signInWithWebAuthn()}>
|
||||
{i18next.t("login:Sign in with WebAuthn")}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
style={{width: "100%", marginBottom: "5px"}}
|
||||
disabled={!application.enablePassword}
|
||||
>
|
||||
{i18next.t("login:Sign in with WebAuthn")}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
{
|
||||
@@ -511,7 +420,7 @@ class LoginPage extends React.Component {
|
||||
<Form.Item>
|
||||
{
|
||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
|
||||
return this.renderProviderLogo(providerItem.provider, application, 30, 5, "small");
|
||||
return ProviderButton.renderProviderLogo(providerItem.provider, application, 30, 5, "small");
|
||||
})
|
||||
}
|
||||
</Form.Item>
|
||||
@@ -532,7 +441,7 @@ class LoginPage extends React.Component {
|
||||
<br />
|
||||
{
|
||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
|
||||
return this.renderProviderLogo(providerItem.provider, application, 40, 10, "big");
|
||||
return ProviderButton.renderProviderLogo(providerItem.provider, application, 40, 10, "big");
|
||||
})
|
||||
}
|
||||
<div>
|
||||
@@ -639,14 +548,14 @@ class LoginPage extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
signInWithWebAuthn() {
|
||||
if (this.state.username === null || this.state.username === "") {
|
||||
signInWithWebAuthn(username) {
|
||||
if (username === null || username === "") {
|
||||
Setting.showMessage("error", "username is required for webauthn login");
|
||||
return;
|
||||
}
|
||||
|
||||
let application = this.getApplicationObj();
|
||||
return fetch(`${Setting.ServerUrl}/api/webauthn/signin/begin?owner=${application.organization}&name=${this.state.username}`, {
|
||||
return fetch(`${Setting.ServerUrl}/api/webauthn/signin/begin?owner=${application.organization}&name=${username}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
})
|
||||
@@ -657,13 +566,13 @@ class LoginPage extends React.Component {
|
||||
throw credentialRequestOptions.status.msg;
|
||||
}
|
||||
|
||||
credentialRequestOptions.certificate.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.certificate.challenge);
|
||||
credentialRequestOptions.certificate.allowCredentials.forEach(function(listItem) {
|
||||
credentialRequestOptions.publicKey.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.publicKey.challenge);
|
||||
credentialRequestOptions.publicKey.allowCredentials.forEach(function(listItem) {
|
||||
listItem.id = UserWebauthnBackend.webAuthnBufferDecode(listItem.id);
|
||||
});
|
||||
|
||||
return navigator.credentials.get({
|
||||
certificate: credentialRequestOptions.certificate
|
||||
publicKey: credentialRequestOptions.publicKey
|
||||
});
|
||||
})
|
||||
.then((assertion) => {
|
||||
|
125
web/src/auth/ProviderButton.js
Normal file
125
web/src/auth/ProviderButton.js
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2021 The Casdoor 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.
|
||||
|
||||
import React from "react";
|
||||
import i18next from "i18next";
|
||||
import * as Provider from "./Provider";
|
||||
import {getProviderLogoURL} from "../Setting";
|
||||
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
|
||||
import QqLoginButton from "./QqLoginButton";
|
||||
import FacebookLoginButton from "./FacebookLoginButton";
|
||||
import WeiboLoginButton from "./WeiboLoginButton";
|
||||
import GiteeLoginButton from "./GiteeLoginButton";
|
||||
import WechatLoginButton from "./WechatLoginButton";
|
||||
import DingTalkLoginButton from "./DingTalkLoginButton";
|
||||
import LinkedInLoginButton from "./LinkedInLoginButton";
|
||||
import WeComLoginButton from "./WeComLoginButton";
|
||||
import LarkLoginButton from "./LarkLoginButton";
|
||||
import GitLabLoginButton from "./GitLabLoginButton";
|
||||
import AdfsLoginButton from "./AdfsLoginButton";
|
||||
import CasdoorLoginButton from "./CasdoorLoginButton";
|
||||
import BaiduLoginButton from "./BaiduLoginButton";
|
||||
import AlipayLoginButton from "./AlipayLoginButton";
|
||||
import InfoflowLoginButton from "./InfoflowLoginButton";
|
||||
import AppleLoginButton from "./AppleLoginButton";
|
||||
import AzureADLoginButton from "./AzureADLoginButton";
|
||||
import SlackLoginButton from "./SlackLoginButton";
|
||||
import SteamLoginButton from "./SteamLoginButton";
|
||||
import BilibiliLoginButton from "./BilibiliLoginButton";
|
||||
import OktaLoginButton from "./OktaLoginButton";
|
||||
import DouyinLoginButton from "./DouyinLoginButton";
|
||||
|
||||
function getSigninButton(type) {
|
||||
const text = i18next.t("login:Sign in with {type}").replace("{type}", type);
|
||||
if (type === "GitHub") {
|
||||
return <GithubLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Google") {
|
||||
return <GoogleLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "QQ") {
|
||||
return <QqLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Facebook") {
|
||||
return <FacebookLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Weibo") {
|
||||
return <WeiboLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Gitee") {
|
||||
return <GiteeLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "WeChat") {
|
||||
return <WechatLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "DingTalk") {
|
||||
return <DingTalkLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "LinkedIn") {
|
||||
return <LinkedInLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "WeCom") {
|
||||
return <WeComLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Lark") {
|
||||
return <LarkLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "GitLab") {
|
||||
return <GitLabLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Adfs") {
|
||||
return <AdfsLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Casdoor") {
|
||||
return <CasdoorLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Baidu") {
|
||||
return <BaiduLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Alipay") {
|
||||
return <AlipayLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Infoflow") {
|
||||
return <InfoflowLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Apple") {
|
||||
return <AppleLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "AzureAD") {
|
||||
return <AzureADLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Slack") {
|
||||
return <SlackLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Steam") {
|
||||
return <SteamLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Bilibili") {
|
||||
return <BilibiliLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Okta") {
|
||||
return <OktaLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Douyin") {
|
||||
return <DouyinLoginButton text={text} align={"center"} />;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
export function renderProviderLogo(provider, application, width, margin, size) {
|
||||
if (size === "small") {
|
||||
if (provider.category === "OAuth") {
|
||||
return (
|
||||
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")}>
|
||||
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} />
|
||||
</a>
|
||||
);
|
||||
} else if (provider.category === "SAML") {
|
||||
return (
|
||||
<a key={provider.displayName} onClick={this.getSamlUrl.bind(this, provider)}>
|
||||
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
return (
|
||||
<div key={provider.displayName} style={{marginBottom: "10px"}}>
|
||||
<a href={Provider.getAuthUrl(application, provider, "signup")}>
|
||||
{
|
||||
getSigninButton(provider.type)
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ import {Link} from "react-router-dom";
|
||||
import {Button, Checkbox, Col, Form, Input, Modal, Result, Row} from "antd";
|
||||
import * as Setting from "../Setting";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import * as ProviderButton from "./ProviderButton";
|
||||
import i18next from "i18next";
|
||||
import * as Util from "./Util";
|
||||
import {authConfig} from "./Auth";
|
||||
@@ -179,6 +180,14 @@ class SignupPage extends React.Component {
|
||||
this.form.current.scrollToField(errorFields[0].name);
|
||||
}
|
||||
|
||||
isProviderVisible(providerItem) {
|
||||
if (this.state.mode === "signup") {
|
||||
return Setting.isProviderVisibleForSignUp(providerItem);
|
||||
} else {
|
||||
return Setting.isProviderVisibleForSignIn(providerItem);
|
||||
}
|
||||
}
|
||||
|
||||
renderFormItem(application, signupItem) {
|
||||
if (!signupItem.visible) {
|
||||
return null;
|
||||
@@ -582,6 +591,11 @@ class SignupPage extends React.Component {
|
||||
{i18next.t("signup:sign in now")}
|
||||
</a>
|
||||
</Form.Item>
|
||||
{
|
||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
|
||||
return ProviderButton.renderProviderLogo(providerItem.provider, application, 30, 5, "small");
|
||||
})
|
||||
}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
@@ -21,15 +21,15 @@ export function registerWebauthnCredential() {
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then((credentialCreationOptions) => {
|
||||
credentialCreationOptions.certificate.challenge = webAuthnBufferDecode(credentialCreationOptions.certificate.challenge);
|
||||
credentialCreationOptions.certificate.user.id = webAuthnBufferDecode(credentialCreationOptions.certificate.user.id);
|
||||
if (credentialCreationOptions.certificate.excludeCredentials) {
|
||||
for (var i = 0; i < credentialCreationOptions.certificate.excludeCredentials.length; i++) {
|
||||
credentialCreationOptions.certificate.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.certificate.excludeCredentials[i].id);
|
||||
credentialCreationOptions.publicKey.challenge = webAuthnBufferDecode(credentialCreationOptions.publicKey.challenge);
|
||||
credentialCreationOptions.publicKey.user.id = webAuthnBufferDecode(credentialCreationOptions.publicKey.user.id);
|
||||
if (credentialCreationOptions.publicKey.excludeCredentials) {
|
||||
for (var i = 0; i < credentialCreationOptions.publicKey.excludeCredentials.length; i++) {
|
||||
credentialCreationOptions.publicKey.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.publicKey.excludeCredentials[i].id);
|
||||
}
|
||||
}
|
||||
return navigator.credentials.create({
|
||||
certificate: credentialCreationOptions.certificate
|
||||
publicKey: credentialCreationOptions.publicKey
|
||||
});
|
||||
})
|
||||
.then((credential) => {
|
||||
|
@@ -25,7 +25,7 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
|
||||
useEffect(() => {
|
||||
switch (captchaType) {
|
||||
case "reCAPTCHA":
|
||||
case "reCAPTCHA": {
|
||||
const reTimer = setInterval(() => {
|
||||
if (!window.grecaptcha) {
|
||||
loadScript("https://recaptcha.net/recaptcha/api.js");
|
||||
@@ -39,7 +39,8 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
}
|
||||
}, 300);
|
||||
break;
|
||||
case "hCaptcha":
|
||||
}
|
||||
case "hCaptcha": {
|
||||
const hTimer = setInterval(() => {
|
||||
if (!window.hcaptcha) {
|
||||
loadScript("https://js.hcaptcha.com/1/api.js");
|
||||
@@ -53,7 +54,8 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
}
|
||||
}, 300);
|
||||
break;
|
||||
case "Aliyun Captcha":
|
||||
}
|
||||
case "Aliyun Captcha": {
|
||||
const AWSCTimer = setInterval(() => {
|
||||
if (!window.AWSC) {
|
||||
loadScript("https://g.alicdn.com/AWSC/AWSC/awsc.js");
|
||||
@@ -76,6 +78,33 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
}
|
||||
}, 300);
|
||||
break;
|
||||
}
|
||||
case "GEETEST": {
|
||||
let getLock = false;
|
||||
const gTimer = setInterval(() => {
|
||||
if (!window.initGeetest4) {
|
||||
loadScript("https://static.geetest.com/v4/gt4.js");
|
||||
}
|
||||
if (window.initGeetest4 && siteKey && !getLock) {
|
||||
const captchaId = String(siteKey);
|
||||
window.initGeetest4({
|
||||
captchaId,
|
||||
product: "float",
|
||||
}, function(captchaObj) {
|
||||
if (!getLock) {
|
||||
captchaObj.appendTo("#captcha");
|
||||
getLock = true;
|
||||
}
|
||||
captchaObj.onSuccess(function() {
|
||||
const result = captchaObj.getValidate();
|
||||
onChange(`lot_number=${result.lot_number}&captcha_output=${result.captcha_output}&pass_token=${result.pass_token}&gen_time=${result.gen_time}&captcha_id=${siteKey}`);
|
||||
});
|
||||
});
|
||||
clearInterval(gTimer);
|
||||
}
|
||||
}, 500);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user