Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions[bot]
08d0269e30 refactor: New Crowdin translations by Github Action (#974)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-08-07 16:06:52 +08:00
leoshine
8e5cd18c91 fix: Restrict the request permissions of providers and applications (#970) 2022-08-07 16:05:05 +08:00
Gucheng Wang
32b4d98c2a Add ExtendProductWithProviders(). 2022-08-07 15:45:06 +08:00
q1anx1
2ea58cd639 chore(style): use gofumpt to fmt go code (#967) 2022-08-07 12:26:14 +08:00
q1anx1
45d2745b67 chore(style): add eslint rules: no-unused-imports and no-unused-vars (#976)
* feat(web): no-unused-imports and no-unused-vars

* chore: fix json style
2022-08-07 11:51:53 +08:00
75 changed files with 437 additions and 324 deletions

View File

@@ -68,7 +68,7 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
Enforcer.ClearPolicy()
//if len(Enforcer.GetPolicy()) == 0 {
// if len(Enforcer.GetPolicy()) == 0 {
if true {
ruleText := `
p, built-in, *, *, *, *, *
@@ -83,7 +83,7 @@ p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, *
p, *, *, *, /api/login/oauth, *, *
p, *, *, GET, /api/get-application, *, *
p, *, *, GET, /api/get-applications, *, *
p, *, *, GET, /api/get-organization-applications, *, *
p, *, *, GET, /api/get-user, *, *
p, *, *, GET, /api/get-user-application, *, *
p, *, *, GET, /api/get-resources, *, *
@@ -92,7 +92,6 @@ p, *, *, POST, /api/buy-product, *, *
p, *, *, GET, /api/get-payment, *, *
p, *, *, POST, /api/update-payment, *, *
p, *, *, POST, /api/invoice-payment, *, *
p, *, *, GET, /api/get-providers, *, *
p, *, *, POST, /api/notify-payment, *, *
p, *, *, POST, /api/unlink, *, *
p, *, *, POST, /api/set-password, *, *

View File

@@ -31,8 +31,7 @@ import (
const AliyunCaptchaVerifyUrl = "http://afs.aliyuncs.com"
type AliyunCaptchaProvider struct {
}
type AliyunCaptchaProvider struct{}
func NewAliyunCaptchaProvider() *AliyunCaptchaProvider {
captcha := &AliyunCaptchaProvider{}

View File

@@ -16,8 +16,7 @@ package captcha
import "github.com/casdoor/casdoor/object"
type DefaultCaptchaProvider struct {
}
type DefaultCaptchaProvider struct{}
func NewDefaultCaptchaProvider() *DefaultCaptchaProvider {
captcha := &DefaultCaptchaProvider{}

View File

@@ -28,8 +28,7 @@ import (
const GEETESTCaptchaVerifyUrl = "http://gcaptcha4.geetest.com/validate"
type GEETESTCaptchaProvider struct {
}
type GEETESTCaptchaProvider struct{}
func NewGEETESTCaptchaProvider() *GEETESTCaptchaProvider {
captcha := &GEETESTCaptchaProvider{}

View File

@@ -25,8 +25,7 @@ import (
const HCaptchaVerifyUrl = "https://hcaptcha.com/siteverify"
type HCaptchaProvider struct {
}
type HCaptchaProvider struct{}
func NewHCaptchaProvider() *HCaptchaProvider {
captcha := &HCaptchaProvider{}

View File

@@ -25,8 +25,7 @@ import (
const ReCaptchaVerifyUrl = "https://recaptcha.net/recaptcha/api/siteverify"
type ReCaptchaProvider struct {
}
type ReCaptchaProvider struct{}
func NewReCaptchaProvider() *ReCaptchaProvider {
captcha := &ReCaptchaProvider{}

View File

@@ -48,8 +48,8 @@ func GetConfigInt64(key string) (int64, error) {
}
func init() {
//this array contains the beego configuration items that may be modified via env
var presetConfigItems = []string{"httpport", "appname"}
// this array contains the beego configuration items that may be modified via env
presetConfigItems := []string{"httpport", "appname"}
for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok {
beego.AppConfig.Set(key, value)

View File

@@ -32,7 +32,7 @@ func TestGetConfString(t *testing.T) {
{"Should be return value", "key", "value"},
}
//do some set up job
// do some set up job
os.Setenv("appname", "casbin")
os.Setenv("key", "value")
@@ -58,7 +58,7 @@ func TestGetConfInt(t *testing.T) {
{"Should be return 8000", "verificationCodeTimeout", 10},
}
//do some set up job
// do some set up job
os.Setenv("httpport", "8001")
err := beego.LoadAppConfig("ini", "app.conf")

View File

@@ -94,6 +94,29 @@ func (c *ApiController) GetUserApplication() {
c.ServeJSON()
}
// GetOrganizationApplications
// @Title GetOrganizationApplications
// @Tag Application API
// @Description get the detail of the organization's application
// @Param organization query string true "The organization name"
// @Success 200 {array} object.Application The Response object
// @router /get-organization-applications [get]
func (c *ApiController) GetOrganizationApplications() {
userId := c.GetSessionUsername()
owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
if organization == "" {
c.ResponseError("Parameter organization is missing")
return
}
var applications []*object.Application
applications = object.GetApplicationsByOrganizationName(owner, organization)
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
}
// UpdateApplication
// @Title UpdateApplication
// @Tag Application API

View File

@@ -44,7 +44,6 @@ func tokenToResponse(token *object.Token) *Response {
return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken}
}
return &Response{Status: "ok", Msg: "", Data: token.AccessToken}
}
// HandleLoggedIn ...
@@ -86,7 +85,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
}
} else if form.Type == ResponseTypeToken || form.Type == ResponseTypeIdToken { //implicit flow
} else if form.Type == ResponseTypeToken || form.Type == ResponseTypeIdToken { // implicit flow
if !object.IsGrantTypeValid(form.Type, application.GrantTypes) {
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
} else {
@@ -94,7 +93,6 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
resp = tokenToResponse(token)
}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
if err != nil {
@@ -103,7 +101,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: redirectUrl}
} else if form.Type == ResponseTypeCas {
//not oauth but CAS SSO protocol
// not oauth but CAS SSO protocol
service := c.Input().Get("service")
resp = wrapErrorResponse(nil)
if service != "" {
@@ -430,7 +428,7 @@ func (c *ApiController) Login() {
} else if provider.Category == "SAML" {
resp = &Response{Status: "error", Msg: "The account does not exist"}
}
//resp = &Response{Status: "ok", Msg: "", Data: res}
// resp = &Response{Status: "ok", Msg: "", Data: res}
} else { // form.Method != "signup"
userId := c.GetSessionUsername()
if userId == "" {

View File

@@ -44,14 +44,13 @@ func (c *RootController) CasValidate() {
return
}
if ok, response, issuedService, _ := object.GetCasTokenByTicket(ticket); ok {
//check whether service is the one for which we previously issued token
// check whether service is the one for which we previously issued token
if issuedService == service {
c.Ctx.Output.Body([]byte(fmt.Sprintf("yes\n%s\n", response.User)))
return
}
}
//token not found
// token not found
c.Ctx.Output.Body([]byte("no\n"))
}
@@ -83,39 +82,39 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
Xmlns: "http://www.yale.edu/tp/cas",
}
//check whether all required parameters are met
// check whether all required parameters are met
if service == "" || ticket == "" {
c.sendCasAuthenticationResponseErr(InvalidRequest, "service and ticket must exist", format)
return
}
ok, response, issuedService, userId := object.GetCasTokenByTicket(ticket)
//find the token
// find the token
if ok {
//check whether service is the one for which we previously issued token
// check whether service is the one for which we previously issued token
if strings.HasPrefix(service, issuedService) {
serviceResponse.Success = response
} else {
//service not match
// service not match
c.sendCasAuthenticationResponseErr(InvalidService, fmt.Sprintf("service %s and %s does not match", service, issuedService), format)
return
}
} else {
//token not found
// token not found
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
return
}
if pgtUrl != "" && serviceResponse.Failure == nil {
//that means we are in proxy web flow
// that means we are in proxy web flow
pgt := object.StoreCasTokenForPgt(serviceResponse.Success, service, userId)
pgtiou := serviceResponse.Success.ProxyGrantingTicket
//todo: check whether it is https
// todo: check whether it is https
pgtUrlObj, err := url.Parse(pgtUrl)
if pgtUrlObj.Scheme != "https" {
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format)
return
}
//make a request to pgturl passing pgt and pgtiou
// make a request to pgturl passing pgt and pgtiou
if err != nil {
c.sendCasAuthenticationResponseErr(InteralError, err.Error(), format)
return
@@ -133,7 +132,7 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
resp, err := http.DefaultClient.Do(request)
if err != nil || !(resp.StatusCode >= 200 && resp.StatusCode < 400) {
//failed to send request
// failed to send request
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, err.Error(), format)
return
}
@@ -184,7 +183,6 @@ func (c *RootController) CasProxy() {
c.Data["xml"] = serviceResponse
c.ServeXML()
}
}
func (c *RootController) SamlValidate() {

View File

@@ -30,7 +30,7 @@ type LdapServer struct {
}
type LdapResp struct {
//Groups []LdapRespGroup `json:"groups"`
// Groups []LdapRespGroup `json:"groups"`
Users []object.LdapRespUser `json:"users"`
}
@@ -88,7 +88,7 @@ func (c *ApiController) GetLdapUser() {
Uid: user.Uid,
Cn: user.Cn,
GroupId: user.GidNumber,
//GroupName: groupsMap[user.GidNumber].Cn,
// GroupName: groupsMap[user.GidNumber].Cn,
Uuid: user.Uuid,
Email: util.GetMaxLenStr(user.Mail, user.Email, user.EmailAddress),
Phone: util.GetMaxLenStr(user.TelephoneNumber, user.Mobile, user.MobileTelephoneNumber),

View File

@@ -58,7 +58,10 @@ func (c *ApiController) GetProducts() {
func (c *ApiController) GetProduct() {
id := c.Input().Get("id")
c.Data["json"] = object.GetProduct(id)
product := object.GetProduct(id)
object.ExtendProductWithProviders(product)
c.Data["json"] = product
c.ServeJSON()
}

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"

View File

@@ -24,7 +24,6 @@ func NewArgon2idCredManager() *Argon2idCredManager {
}
func (cm *Argon2idCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
if err != nil {
return ""

View File

@@ -17,6 +17,7 @@ package cred
import (
"crypto/sha256"
"encoding/base64"
"golang.org/x/crypto/pbkdf2"
)
@@ -36,4 +37,4 @@ func (cm *Pbkdf2SaltCredManager) GetHashedPassword(password string, userSalt str
func (cm *Pbkdf2SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt, organizationSalt)
}
}

View File

@@ -59,12 +59,12 @@ func (idp *AdfsIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *AdfsIdProvider) getConfig(hostUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: fmt.Sprintf("%s/adfs/oauth2/authorize", hostUrl),
TokenURL: fmt.Sprintf("%s/adfs/oauth2/token", hostUrl),
}
var config = &oauth2.Config{
config := &oauth2.Config{
Endpoint: endpoint,
}

View File

@@ -56,12 +56,12 @@ func (idp *AlipayIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *AlipayIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm",
TokenURL: "https://openapi.alipay.com/gateway.do",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"", ""},
Endpoint: endpoint,
ClientID: clientId,
@@ -206,7 +206,6 @@ func (idp *AlipayIdProvider) postWithBody(body interface{}, targetUrl string) ([
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

View File

@@ -46,12 +46,12 @@ func (idp *BaiduIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *BaiduIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
TokenURL: "https://openapi.baidu.com/oauth/2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email"},
Endpoint: endpoint,
}

View File

@@ -47,12 +47,12 @@ func (idp *BilibiliIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *BilibiliIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://api.bilibili.com/x/account-oauth2/v1/token",
AuthURL: "http://member.bilibili.com/arcopen/fn/user/account/info",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"", ""},
Endpoint: endpoint,
ClientID: clientId,
@@ -104,7 +104,6 @@ func (idp *BilibiliIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
data, err := idp.postWithBody(pTokenParams, idp.Config.Endpoint.TokenURL)
if err != nil {
return nil, err
}
@@ -167,7 +166,6 @@ func (idp *BilibiliIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
userInfoUrl := fmt.Sprintf("%s?%s", idp.Config.Endpoint.AuthURL, params.Encode())
resp, err := idp.Client.Get(userInfoUrl)
if err != nil {
return nil, err
}

View File

@@ -72,7 +72,6 @@ func (idp *CasdoorIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
@@ -82,7 +81,7 @@ func (idp *CasdoorIdProvider) GetToken(code string) (*oauth2.Token, error) {
return nil, err
}
//check if token is expired
// check if token is expired
if pToken.ExpiresIn <= 0 {
return nil, fmt.Errorf("%s", pToken.AccessToken)
}
@@ -91,7 +90,6 @@ func (idp *CasdoorIdProvider) GetToken(code string) (*oauth2.Token, error) {
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
}
return token, nil
}
/*
@@ -125,7 +123,7 @@ func (idp *CasdoorIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error
if err != nil {
return nil, err
}
//add accesstoken to bearer token
// add accesstoken to bearer token
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
resp, err := idp.Client.Do(request)
if err != nil {
@@ -155,5 +153,4 @@ func (idp *CasdoorIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error
AvatarUrl: cdUserinfo.AvatarUrl,
}
return userInfo, nil
}

View File

@@ -36,7 +36,7 @@ func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl strin
idp := &CustomIdProvider{}
idp.UserInfoUrl = userInfoUrl
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -76,7 +76,7 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
if err != nil {
return nil, err
}
//add accessToken to request header
// add accessToken to request header
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
resp, err := idp.Client.Do(request)
if err != nil {

View File

@@ -48,12 +48,12 @@ func (idp *DingTalkIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *DingTalkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://api.dingtalk.com/v1.0/contact/users/me",
TokenURL: "https://api.dingtalk.com/v1.0/oauth2/userAccessToken",
}
var config = &oauth2.Config{
config := &oauth2.Config{
// DingTalk not allow to set scopes,here it is just a placeholder,
// convenient to use later
Scopes: []string{"", ""},

View File

@@ -42,12 +42,12 @@ func (idp *DouyinIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *DouyinIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://open.douyin.com/oauth/access_token",
AuthURL: "https://open.douyin.com/platform/oauth/connect",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user_info"},
Endpoint: endpoint,
ClientID: clientId,

View File

@@ -46,11 +46,11 @@ func (idp *FacebookIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *FacebookIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.facebook.com/oauth/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email,public_profile"},
Endpoint: endpoint,
ClientID: clientId,
@@ -62,9 +62,9 @@ func (idp *FacebookIdProvider) getConfig(clientId string, clientSecret string, r
}
type FacebookAccessToken struct {
AccessToken string `json:"access_token"` //Interface call credentials
TokenType string `json:"token_type"` //Access token type
ExpiresIn int64 `json:"expires_in"` //access_token interface call credential timeout time, unit (seconds)
AccessToken string `json:"access_token"` // Interface call credentials
TokenType string `json:"token_type"` // Access token type
ExpiresIn int64 `json:"expires_in"` // access_token interface call credential timeout time, unit (seconds)
}
type FacebookCheckToken struct {

View File

@@ -49,11 +49,11 @@ func (idp *GiteeIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *GiteeIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://gitee.com/oauth/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user_info emails"},
Endpoint: endpoint,

View File

@@ -49,12 +49,12 @@ func (idp *GithubIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *GithubIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user:email", "read:user"},
Endpoint: endpoint,
}
@@ -93,7 +93,6 @@ func (idp *GithubIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
return token, nil
}
//{

View File

@@ -46,11 +46,11 @@ func (idp *GitlabIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *GitlabIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://gitlab.com/oauth/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"read_user+profile"},
Endpoint: endpoint,
ClientID: clientId,

View File

@@ -47,12 +47,12 @@ func (idp *GoogleIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *GoogleIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"profile", "email"},
Endpoint: endpoint,
}

View File

@@ -207,7 +207,7 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
return &idp
}
//Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
// Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
idpClient.Set(reflect.ValueOf(client))
@@ -225,8 +225,8 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
return nil, err
}
} else {
//Need to construct variables supported by goth
//to call the function to obtain accessToken
// Need to construct variables supported by goth
// to call the function to obtain accessToken
value = url.Values{}
value.Add("code", code)
}
@@ -235,7 +235,7 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
return nil, err
}
//Get ExpiresAt's value
// Get ExpiresAt's value
valueOfExpire := reflect.ValueOf(idp.Session).Elem().FieldByName("ExpiresAt")
if valueOfExpire.IsValid() {
expireAt = valueOfExpire.Interface().(time.Time)
@@ -264,8 +264,8 @@ func getUser(gothUser goth.User, provider string) *UserInfo {
Email: gothUser.Email,
AvatarUrl: gothUser.AvatarURL,
}
//Some idp return an empty Name
//so construct the Name with firstname and lastname or nickname
// Some idp return an empty Name
// so construct the Name with firstname and lastname or nickname
if user.Username == "" {
if gothUser.FirstName != "" && gothUser.LastName != "" {
user.Username = getName(gothUser.FirstName, gothUser.LastName)

View File

@@ -43,7 +43,7 @@ func (idp *InfoflowInternalIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *InfoflowInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -139,7 +139,7 @@ type InfoflowInternalUserInfo struct {
// get more detail via: https://qy.baidu.com/doc/index.html#/inner_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
//Get userid first
// Get userid first
accessToken := token.AccessToken
code := token.Extra("code").(string)
resp, err := idp.Client.Get(fmt.Sprintf("https://qy.im.baidu.com/api/user/getuserinfo?access_token=%s&code=%s&agentid=%s", accessToken, code, idp.AgentId))
@@ -159,7 +159,7 @@ func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserIn
if userResp.Errcode != 0 {
return nil, fmt.Errorf("userIdResp.Errcode = %d, userIdResp.Errmsg = %s", userResp.Errcode, userResp.Errmsg)
}
//Use userid and accesstoken to get user information
// Use userid and accesstoken to get user information
resp, err = idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
if err != nil {
return nil, err

View File

@@ -47,7 +47,7 @@ func (idp *InfoflowIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *InfoflowIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -136,7 +136,7 @@ type InfoflowUserInfo struct {
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
//Get userid first
// Get userid first
accessToken := token.AccessToken
code := token.Extra("code").(string)
resp, err := idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/getuserinfo?access_token=%s&code=%s&agentid=%s", accessToken, code, idp.AgentId))
@@ -156,7 +156,7 @@ func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
if userResp.Errcode != 0 {
return nil, fmt.Errorf("userIdResp.Errcode = %d, userIdResp.Errmsg = %s", userResp.Errcode, userResp.Errmsg)
}
//Use userid and accesstoken to get user information
// Use userid and accesstoken to get user information
resp, err = idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
if err != nil {
return nil, err

View File

@@ -45,11 +45,11 @@ func (idp *LarkIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *LarkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{},
Endpoint: endpoint,
ClientID: clientId,

View File

@@ -47,11 +47,11 @@ func (idp *LinkedInIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *LinkedInIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://www.linkedIn.com/oauth/v2/accessToken",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email,public_profile"},
Endpoint: endpoint,
ClientID: clientId,
@@ -63,8 +63,8 @@ func (idp *LinkedInIdProvider) getConfig(clientId string, clientSecret string, r
}
type LinkedInAccessToken struct {
AccessToken string `json:"access_token"` //Interface call credentials
ExpiresIn int64 `json:"expires_in"` //access_token interface call credential timeout time, unit (seconds)
AccessToken string `json:"access_token"` // Interface call credentials
ExpiresIn int64 `json:"expires_in"` // access_token interface call credential timeout time, unit (seconds)
}
// GetToken use code get access_token (*operation of getting code ought to be done in front)

View File

@@ -48,12 +48,12 @@ func (idp *OktaIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *OktaIdProvider) getConfig(hostUrl string, clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: fmt.Sprintf("%s/v1/token", hostUrl),
AuthURL: fmt.Sprintf("%s/v1/authorize", hostUrl),
}
var config = &oauth2.Config{
config := &oauth2.Config{
// openid is required for authentication requests
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#reserved-scopes
Scopes: []string{"openid", "profile", "email"},

View File

@@ -48,11 +48,11 @@ func (idp *QqIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *QqIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"get_user_info"},
Endpoint: endpoint,
}

View File

@@ -47,11 +47,11 @@ func (idp *WeChatIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeChatIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"snsapi_login"},
Endpoint: endpoint,
ClientID: clientId,
@@ -63,12 +63,12 @@ func (idp *WeChatIdProvider) getConfig(clientId string, clientSecret string, red
}
type WechatAccessToken struct {
AccessToken string `json:"access_token"` //Interface call credentials
ExpiresIn int64 `json:"expires_in"` //access_token interface call credential timeout time, unit (seconds)
RefreshToken string `json:"refresh_token"` //User refresh access_token
Openid string `json:"openid"` //Unique ID of authorized user
Scope string `json:"scope"` //The scope of user authorization, separated by commas. (,)
Unionid string `json:"unionid"` //This field will appear if and only if the website application has been authorized by the user's UserInfo.
AccessToken string `json:"access_token"` // Interface call credentials
ExpiresIn int64 `json:"expires_in"` // access_token interface call credential timeout time, unit (seconds)
RefreshToken string `json:"refresh_token"` // User refresh access_token
Openid string `json:"openid"` // Unique ID of authorized user
Scope string `json:"scope"` // The scope of user authorization, separated by commas. (,)
Unionid string `json:"unionid"` // This field will appear if and only if the website application has been authorized by the user's UserInfo.
}
// GetToken use code get access_token (*operation of getting code ought to be done in front)

View File

@@ -42,7 +42,7 @@ func (idp *WeChatMiniProgramIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *WeChatMiniProgramIdProvider) getConfig(clientId string, clientSecret string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
}
@@ -78,5 +78,4 @@ func (idp *WeChatMiniProgramIdProvider) GetSessionByCode(code string) (*WeChatMi
return nil, fmt.Errorf("err: %s", session.Errmsg)
}
return &session, nil
}

View File

@@ -24,7 +24,7 @@ import (
"golang.org/x/oauth2"
)
//This idp is using wecom internal application api as idp
// This idp is using wecom internal application api as idp
type WeComInternalIdProvider struct {
Client *http.Client
Config *oauth2.Config
@@ -44,7 +44,7 @@ func (idp *WeComInternalIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *WeComInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -115,7 +115,7 @@ type WecomInternalUserInfo struct {
}
func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
//Get userid first
// Get userid first
accessToken := token.AccessToken
code := token.Extra("code").(string)
resp, err := idp.Client.Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=%s&code=%s", accessToken, code))
@@ -138,7 +138,7 @@ func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo,
if userResp.OpenId != "" {
return nil, fmt.Errorf("not an internal user")
}
//Use userid and accesstoken to get user information
// Use userid and accesstoken to get user information
resp, err = idp.Client.Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
if err != nil {
return nil, err

View File

@@ -46,11 +46,11 @@ func (idp *WeComIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeComIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"snsapi_login"},
Endpoint: endpoint,
ClientID: clientId,

View File

@@ -48,11 +48,11 @@ func (idp *WeiBoIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeiBoIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://api.weibo.com/oauth2/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{""},
Endpoint: endpoint,
ClientID: clientId,

View File

@@ -42,9 +42,9 @@ func main() {
proxy.InitHttpClient()
authz.InitAuthz()
util.SafeGoroutine(func() {object.RunSyncUsersJob()})
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
//beego.DelStaticPath("/static")
// beego.DelStaticPath("/static")
beego.SetStaticPath("/static", "web/build/static")
beego.BConfig.WebConfig.DirectoryIndex = true
beego.SetStaticPath("/swagger", "swagger")
@@ -66,14 +66,14 @@ func main() {
beego.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint")
}
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
//beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
// beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
err := logs.SetLogger("file", `{"filename":"logs/casdoor.log","maxdays":99999,"perm":"0770"}`)
if err != nil {
panic(err)
}
port := beego.AppConfig.DefaultInt("httpport", 8000)
//logs.SetLevel(logs.LevelInformational)
// logs.SetLevel(logs.LevelInformational)
logs.SetLogFuncCall(false)
beego.Run(fmt.Sprintf(":%v", port))
}

View File

@@ -317,7 +317,7 @@ func (application *Application) GetId() string {
}
func CheckRedirectUriValid(application *Application, redirectUri string) bool {
var validUri = false
validUri := false
for _, tmpUri := range application.RedirectUris {
if strings.Contains(redirectUri, tmpUri) {
validUri = true

View File

@@ -182,7 +182,7 @@ func CheckUserPassword(organization string, username string, password string) (*
}
if user.Ldap != "" {
//ONLY for ldap users
// ONLY for ldap users
return checkLdapUserPassword(user, password)
} else {
msg := CheckPassword(user, password)

View File

@@ -56,7 +56,7 @@ type ldapUser struct {
Uid string
Cn string
GidNumber string
//Gcn string
// Gcn string
Uuid string
Mail string
Email string
@@ -73,7 +73,7 @@ type LdapRespUser struct {
Uid string `json:"uid"`
Cn string `json:"cn"`
GroupId string `json:"groupId"`
//GroupName string `json:"groupName"`
// GroupName string `json:"groupName"`
Uuid string `json:"uuid"`
Email string `json:"email"`
Phone string `json:"phone"`
@@ -208,11 +208,15 @@ func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*
func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
SearchFilter := "(objectClass=posixAccount)"
SearchAttributes := []string{"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
SearchAttributes := []string{
"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
SearchFilterMsAD := "(objectClass=user)"
SearchAttributesMsAD := []string{"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
SearchAttributesMsAD := []string{
"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
var searchReq *goldap.SearchRequest
if l.IsAD {
searchReq = goldap.NewSearchRequest(baseDn,
@@ -459,7 +463,7 @@ func CheckLdapUuidExist(owner string, uuids []string) []string {
}
}
for uuid, _ := range existUuidSet {
for uuid := range existUuidSet {
existUuids = append(existUuids, uuid)
}
return existUuids

View File

@@ -31,7 +31,7 @@ func GetLdapAutoSynchronizer() *LdapAutoSynchronizer {
return globalLdapAutoSynchronizer
}
//start autosync for specified ldap, old existing autosync goroutine will be ceased
// start autosync for specified ldap, old existing autosync goroutine will be ceased
func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
l.Lock()
defer l.Unlock()
@@ -48,7 +48,7 @@ func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
stopChan := make(chan struct{})
l.ldapIdToStopChan[ldapId] = stopChan
logs.Info(fmt.Sprintf("autoSync started for %s", ldap.Id))
util.SafeGoroutine(func() {l.syncRoutine(ldap, stopChan)})
util.SafeGoroutine(func() { l.syncRoutine(ldap, stopChan) })
return nil
}
@@ -61,7 +61,7 @@ func (l *LdapAutoSynchronizer) StopAutoSync(ldapId string) {
}
}
//autosync goroutine
// autosync goroutine
func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
ticker := time.NewTicker(time.Duration(ldap.AutoSync) * time.Minute)
defer ticker.Stop()
@@ -74,7 +74,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
}
UpdateLdapSyncTime(ldap.Id)
//fetch all users
// fetch all users
conn, err := GetLdapConn(ldap.Host, ldap.Port, ldap.Admin, ldap.Passwd)
if err != nil {
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
@@ -93,10 +93,9 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(*existed), len(*existed)))
}
}
}
//start all autosync goroutine for existing ldap servers in each organizations
// start all autosync goroutine for existing ldap servers in each organizations
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
organizations := []*Organization{}
err := adapter.Engine.Desc("created_time").Find(&organizations)

View File

@@ -27,8 +27,8 @@ type Model struct {
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
ModelText string `xorm:"mediumtext" json:"modelText"`
IsEnabled bool `json:"isEnabled"`
ModelText string `xorm:"mediumtext" json:"modelText"`
IsEnabled bool `json:"isEnabled"`
}
func GetModelCount(owner, field, value string) int {

View File

@@ -93,9 +93,9 @@ func GetOidcDiscovery(host string) OidcDiscovery {
func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
certs := GetCerts("admin")
jwks := jose.JSONWebKeySet{}
//follows the protocol rfc 7517(draft)
//link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
// follows the protocol rfc 7517(draft)
// link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
// or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
for _, cert := range certs {
certPemBlock := []byte(cert.Certificate)
certDerBlock, _ := pem.Decode(certPemBlock)

View File

@@ -38,6 +38,8 @@ type Product struct {
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
State string `xorm:"varchar(100)" json:"state"`
ProviderObjs []*Provider `xorm:"-" json:"providerObjs"`
}
func GetProductCount(owner, field, value string) int {
@@ -209,3 +211,14 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
return payUrl, err
}
func ExtendProductWithProviders(product *Product) {
product.ProviderObjs = []*Provider{}
m := getProviderMap(product.Owner)
for _, providerItem := range product.Providers {
if provider, ok := m[providerItem]; ok {
product.ProviderObjs = append(product.ProviderObjs, provider)
}
}
}

View File

@@ -29,7 +29,7 @@ func init() {
var err error
logPostOnly, err = conf.GetConfigBool("logPostOnly")
if err != nil {
//panic(err)
// panic(err)
}
}

View File

@@ -110,7 +110,7 @@ func UpdateResource(id string, resource *Resource) bool {
panic(err)
}
//return affected != 0
// return affected != 0
return true
}

View File

@@ -35,7 +35,7 @@ import (
uuid "github.com/satori/go.uuid"
)
//returns a saml2 response
// returns a saml2 response
func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
samlResponse := &etree.Element{
Space: "samlp",
@@ -100,7 +100,6 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
displayName.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(user.DisplayName)
return samlResponse, nil
}
type X509Key struct {
@@ -114,7 +113,7 @@ func (x X509Key) GetKeyPair() (privateKey *rsa.PrivateKey, cert []byte, err erro
return privateKey, cert, err
}
//SAML METADATA
// SAML METADATA
type IdpEntityDescriptor struct {
XMLName xml.Name `xml:"EntityDescriptor"`
DS string `xml:"xmlns:ds,attr"`
@@ -299,7 +298,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
Space: "samlp",
Tag: "Response",
}
//create samlresponse
// create samlresponse
samlResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:1.0:protocol")
samlResponse.CreateAttr("MajorVersion", "1")
samlResponse.CreateAttr("MinorVersion", "1")
@@ -315,7 +314,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
samlResponse.CreateElement("samlp:Status").CreateElement("samlp:StatusCode").CreateAttr("Value", "samlp:Success")
//create assertion which is inside the response
// create assertion which is inside the response
assertion := samlResponse.CreateElement("saml:Assertion")
assertion.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:1.0:assertion")
assertion.CreateAttr("MajorVersion", "1")
@@ -328,19 +327,19 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
condition.CreateAttr("NotBefore", now)
condition.CreateAttr("NotOnOrAfter", expireTime)
//AuthenticationStatement inside assertion
// AuthenticationStatement inside assertion
authenticationStatement := assertion.CreateElement("saml:AuthenticationStatement")
authenticationStatement.CreateAttr("AuthenticationMethod", "urn:oasis:names:tc:SAML:1.0:am:password")
authenticationStatement.CreateAttr("AuthenticationInstant", now)
//subject inside AuthenticationStatement
// subject inside AuthenticationStatement
subject := assertion.CreateElement("saml:Subject")
//nameIdentifier inside subject
// nameIdentifier inside subject
nameIdentifier := subject.CreateElement("saml:NameIdentifier")
//nameIdentifier.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
// nameIdentifier.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
nameIdentifier.SetText(user.Name)
//subjectConfirmation inside subject
// subjectConfirmation inside subject
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
subjectConfirmation.CreateElement("saml:ConfirmationMethod").SetText("urn:oasis:names:tc:SAML:1.0:cm:artifact")

View File

@@ -31,7 +31,7 @@ func init() {
var err error
isCloudIntranet, err = conf.GetConfigBool("isCloudIntranet")
if err != nil {
//panic(err)
// panic(err)
}
}

View File

@@ -220,7 +220,7 @@ func DeleteTokenByAceessToken(accessToken string) (bool, *Application) {
}
func GetTokenByAccessToken(accessToken string) *Token {
//Check if the accessToken is in the database
// Check if the accessToken is in the database
token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token)
if err != nil || !existed {
@@ -330,7 +330,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
}
}
//Check if grantType is allowed in the current application
// Check if grantType is allowed in the current application
if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" {
return &TokenError{
@@ -688,7 +688,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
ErrorDescription: "the application does not allow to sign up new account",
}
}
//Add new user
// Add new user
var name string
if username != "" {
name = username
@@ -729,7 +729,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
Application: application.Name,
Organization: user.Owner,
User: user.Name,
Code: session.SessionKey, //a trick, because miniprogram does not use the code, so use the code field to save the session_key
Code: session.SessionKey, // a trick, because miniprogram does not use the code, so use the code field to save the session_key
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * 60,

View File

@@ -88,7 +88,7 @@ type CasAnyAttribute struct {
type CasAuthenticationSuccessWrapper struct {
AuthenticationSuccess *CasAuthenticationSuccess // the token we issued
Service string //to which service this token is issued
Service string // to which service this token is issued
UserId string
}
@@ -116,10 +116,10 @@ type Saml11AssertionArtifact struct {
InnerXML string `xml:",innerxml"`
}
//st is short for service ticket
// st is short for service ticket
var stToServiceResponse sync.Map
//pgt is short for proxy granting ticket
// pgt is short for proxy granting ticket
var pgtToServiceResponse sync.Map
func StoreCasTokenForPgt(token *CasAuthenticationSuccess, service, userId string) string {
@@ -262,12 +262,11 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
return "", "", fmt.Errorf("err: %s", err.Error())
}
return xmlStr, service, nil
}
func (c *CasAuthenticationSuccess) DeepCopy() CasAuthenticationSuccess {
res := *c
//copy proxy
// copy proxy
if c.Proxies != nil {
tmp := c.Proxies.DeepCopy()
res.Proxies = &tmp
@@ -307,7 +306,6 @@ func (c *CasAttributes) DeepCopy() CasAttributes {
res.ExtraAttributes[i] = &tmp
}
return res
}
func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
@@ -316,11 +314,11 @@ func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
Attributes: make([]*CasNamedAttribute, len(c.Attributes)),
}
for i, a := range c.AnyAttributes {
var tmp = *a
tmp := *a
res.AnyAttributes[i] = &tmp
}
for i, a := range c.Attributes {
var tmp = *a
tmp := *a
res.Attributes[i] = &tmp
}
return res

View File

@@ -46,7 +46,7 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
}
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
//pp.Client.DebugSwitch = gopay.DebugOn
// pp.Client.DebugSwitch = gopay.DebugOn
bm := gopay.BodyMap{}

View File

@@ -25,8 +25,10 @@ import (
"golang.org/x/net/proxy"
)
var DefaultHttpClient *http.Client
var ProxyHttpClient *http.Client
var (
DefaultHttpClient *http.Client
ProxyHttpClient *http.Client
)
func InitHttpClient() {
// not use proxy

View File

@@ -78,7 +78,7 @@ func getObject(ctx *context.Context) (string, string) {
var obj Object
err := json.Unmarshal(body, &obj)
if err != nil {
//panic(err)
// panic(err)
return "", ""
}

View File

@@ -71,5 +71,4 @@ func AutoSigninFilter(ctx *context.Context) {
setSessionUser(ctx, userId)
return
}
}

View File

@@ -30,19 +30,18 @@ func init() {
}
func initAPI() {
ns :=
beego.NewNamespace("/",
beego.NSNamespace("/api",
beego.NSInclude(
&controllers.ApiController{},
),
ns := beego.NewNamespace("/",
beego.NSNamespace("/api",
beego.NSInclude(
&controllers.ApiController{},
),
beego.NSNamespace("",
beego.NSInclude(
&controllers.RootController{},
),
),
beego.NSNamespace("",
beego.NSInclude(
&controllers.RootController{},
),
)
),
)
beego.AddNamespace(ns)
beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup")
@@ -116,6 +115,7 @@ func initAPI() {
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication")
beego.Router("/api/get-organization-applications", &controllers.ApiController{}, "GET:GetOrganizationApplications")
beego.Router("/api/update-application", &controllers.ApiController{}, "POST:UpdateApplication")
beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication")
beego.Router("/api/delete-application", &controllers.ApiController{}, "POST:DeleteApplication")
@@ -195,5 +195,4 @@ func initAPI() {
beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
}

View File

@@ -1291,6 +1291,35 @@
}
}
},
"/api/get-organization-applications": {
"get": {
"tags": [
"Application API"
],
"description": "get the detail of the organization's application",
"operationId": "ApiController.GetOrganizationApplications",
"parameters": [
{
"in": "query",
"name": "organization",
"description": "The organization name",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Application"
}
}
}
}
}
},
"/api/get-organizations": {
"get": {
"tags": [
@@ -1853,6 +1882,24 @@
"description": "The id of the user",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "owner",
"description": "The owner of the user",
"type": "string"
},
{
"in": "query",
"name": "email",
"description": "The email of the user",
"type": "string"
},
{
"in": "query",
"name": "phone",
"description": "The phone of the user",
"type": "string"
}
],
"responses": {
@@ -3220,11 +3267,11 @@
}
},
"definitions": {
"2127.0xc000427560.false": {
"2200.0xc0003f8480.false": {
"title": "false",
"type": "object"
},
"2161.0xc000427590.false": {
"2235.0xc0003f84b0.false": {
"title": "false",
"type": "object"
},
@@ -3342,10 +3389,10 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/2127.0xc000427560.false"
"$ref": "#/definitions/2200.0xc0003f8480.false"
},
"data2": {
"$ref": "#/definitions/2161.0xc000427590.false"
"$ref": "#/definitions/2235.0xc0003f84b0.false"
},
"msg": {
"type": "string"
@@ -3549,6 +3596,9 @@
"type": "integer",
"format": "int64"
},
"certificate": {
"type": "string"
},
"createdTime": {
"type": "string"
},
@@ -3571,9 +3621,6 @@
"privateKey": {
"type": "string"
},
"certificate": {
"type": "string"
},
"scope": {
"type": "string"
},
@@ -4585,6 +4632,12 @@
"permanentAvatar": {
"type": "string"
},
"permissions": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
},
"phone": {
"type": "string"
},
@@ -4606,6 +4659,12 @@
"region": {
"type": "string"
},
"roles": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Role"
}
},
"score": {
"type": "integer",
"format": "int64"

View File

@@ -837,6 +837,25 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/object.Organization'
/api/get-organization-applications:
get:
tags:
- Application API
description: get the detail of the organization's application
operationId: ApiController.GetOrganizationApplications
parameters:
- in: query
name: organization
description: The organization name
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Application'
/api/get-organizations:
get:
tags:
@@ -1209,6 +1228,18 @@ paths:
description: The id of the user
required: true
type: string
- in: query
name: owner
description: The owner of the user
type: string
- in: query
name: email
description: The email of the user
type: string
- in: query
name: phone
description: The phone of the user
type: string
responses:
"200":
description: The Response object
@@ -2108,10 +2139,10 @@ paths:
schema:
$ref: '#/definitions/Response'
definitions:
2127.0xc000427560.false:
2200.0xc0003f8480.false:
title: "false"
type: object
2161.0xc000427590.false:
2235.0xc0003f84b0.false:
title: "false"
type: object
Response:
@@ -2192,9 +2223,9 @@ definitions:
type: object
properties:
data:
$ref: '#/definitions/2127.0xc000427560.false'
$ref: '#/definitions/2200.0xc0003f8480.false'
data2:
$ref: '#/definitions/2161.0xc000427590.false'
$ref: '#/definitions/2235.0xc0003f84b0.false'
msg:
type: string
name:
@@ -2331,6 +2362,8 @@ definitions:
bitSize:
type: integer
format: int64
certificate:
type: string
createdTime:
type: string
cryptoAlgorithm:
@@ -2346,8 +2379,6 @@ definitions:
type: string
privateKey:
type: string
certificate:
type: string
scope:
type: string
type:
@@ -3027,6 +3058,10 @@ definitions:
type: string
permanentAvatar:
type: string
permissions:
type: array
items:
$ref: '#/definitions/object.Permission'
phone:
type: string
preHash:
@@ -3041,6 +3076,10 @@ definitions:
format: int64
region:
type: string
roles:
type: array
items:
$ref: '#/definitions/object.Role'
score:
type: integer
format: int64

View File

@@ -32,7 +32,7 @@ func GetIPInfo(clientIP string) string {
res := ""
for i := range ips {
ip := strings.TrimSpace(ips[i])
//desc := GetDescFromIP(ip)
// desc := GetDescFromIP(ip)
ipstr := fmt.Sprintf("%s: %s", ip, "")
if i != len(ips)-1 {
res += ipstr + " -> "

View File

@@ -19,8 +19,10 @@ import (
"regexp"
)
var rePhoneCn *regexp.Regexp
var rePhone *regexp.Regexp
var (
rePhoneCn *regexp.Regexp
rePhone *regexp.Regexp
)
func init() {
// https://learnku.com/articles/31543

View File

@@ -69,7 +69,7 @@ func BoolToString(b bool) string {
return "0"
}
//CamelToSnakeCase This function transform camelcase in snakecase LoremIpsum in lorem_ipsum
// CamelToSnakeCase This function transform camelcase in snakecase LoremIpsum in lorem_ipsum
func CamelToSnakeCase(camel string) string {
var buf bytes.Buffer
for _, c := range camel {
@@ -177,7 +177,7 @@ func ReadStringFromPath(path string) string {
}
func WriteStringToPath(s string, path string) {
err := ioutil.WriteFile(path, []byte(s), 0644)
err := ioutil.WriteFile(path, []byte(s), 0o644)
if err != nil {
panic(err)
}

View File

@@ -15,9 +15,10 @@
package util
import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"testing"
)
func TestParseInt(t *testing.T) {
@@ -245,4 +246,3 @@ func TestSnakeString(t *testing.T) {
})
}
}

View File

@@ -31,7 +31,6 @@ func Test_GetCurrentTime(t *testing.T) {
types := reflect.TypeOf(test).Kind()
assert.Equal(t, types, reflect.String, "GetCurrentUnixTime should be return string")
}
func Test_GetCurrentUnixTime_Shoud_Return_String(t *testing.T) {
@@ -41,7 +40,6 @@ func Test_GetCurrentUnixTime_Shoud_Return_String(t *testing.T) {
}
func Test_IsTokenExpired(t *testing.T) {
type input struct {
createdTime string
expiresIn int

View File

@@ -17,6 +17,7 @@
"version": "detect"
}
},
"plugins": ["unused-imports"],
"extends": ["eslint:recommended", "plugin:react/recommended"],
"rules": {
// "eqeqeq": "error",
@@ -45,30 +46,48 @@
"curly": ["error", "all"],
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"no-mixed-spaces-and-tabs": "error",
"sort-imports": ["error", {
"ignoreDeclarationSort": true
}],
"no-multiple-empty-lines": ["error", { "max": 1, "maxBOF": 0, "maxEOF": 0 }],
"sort-imports": [
"error",
{
"ignoreDeclarationSort": true
}
],
"no-multiple-empty-lines": [
"error",
{ "max": 1, "maxBOF": 0, "maxEOF": 0 }
],
"space-unary-ops": ["error", { "words": true, "nonwords": false }],
"space-infix-ops": "error",
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
"comma-style": ["error", "last"],
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "never",
"exports": "never",
"functions": "never"
}],
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "never",
"exports": "never",
"functions": "never"
}
],
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"error",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "none",
"argsIgnorePattern": "^_"
}
],
"react/prop-types": "off",
"react/display-name": "off",
"react/react-in-jsx-scope": "off",
// don't use strict mod now, otherwise there are a lot of errors in the codebase
"no-unused-vars": "warn",
"no-unused-vars": "off",
"react/no-deprecated": "warn",
"no-case-declarations": "warn",
"react/jsx-key": "warn"

View File

@@ -12,13 +12,16 @@
"antd": "^4.15.5",
"codemirror": "^5.61.1",
"copy-to-clipboard": "^3.3.1",
"core-js": "^3.21.1",
"craco-less": "^1.17.1",
"eslint-plugin-unused-imports": "^2.0.0",
"file-saver": "^2.0.5",
"i18n-iso-countries": "^7.0.0",
"i18next": "^19.8.9",
"moment": "^2.29.1",
"qs": "^6.10.2",
"react": "^17.0.2",
"react-app-polyfill": "^3.0.0",
"react-codemirror2": "^7.2.1",
"react-cropper": "^2.1.7",
"react-device-detect": "^1.14.0",
@@ -29,9 +32,7 @@
"react-i18next": "^11.8.7",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-social-login-buttons": "^3.4.0",
"react-app-polyfill": "^3.0.0",
"core-js": "^3.21.1"
"react-social-login-buttons": "^3.4.0"
},
"scripts": {
"start": "cross-env PORT=7001 craco start",

View File

@@ -16,7 +16,6 @@ import React from "react";
import {Button, Descriptions, Spin} from "antd";
import i18next from "i18next";
import * as ProductBackend from "./backend/ProductBackend";
import * as ProviderBackend from "./backend/ProviderBackend";
import * as Setting from "./Setting";
class ProductBuyPage extends React.Component {
@@ -26,14 +25,12 @@ class ProductBuyPage extends React.Component {
classes: props,
productName: props.match?.params.productName,
product: null,
providers: [],
isPlacingOrder: false,
};
}
UNSAFE_componentWillMount() {
this.getProduct();
this.getPaymentProviders();
}
getProduct() {
@@ -45,15 +42,6 @@ class ProductBuyPage extends React.Component {
});
}
getPaymentProviders() {
ProviderBackend.getProviders("admin")
.then((res) => {
this.setState({
providers: res.filter(provider => provider.category === "Payment"),
});
});
}
getProductObj() {
if (this.props.product !== undefined) {
return this.props.product;
@@ -86,32 +74,6 @@ class ProductBuyPage extends React.Component {
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
}
getProviders(product) {
if (this.state.providers.length === 0 || product.providers.length === 0) {
return [];
}
let providerMap = {};
this.state.providers.forEach(provider => {
providerMap[provider.name] = provider;
});
return product.providers.map(providerName => providerMap[providerName]);
}
getPayUrl(product, provider) {
if (product === null || provider === null) {
return "";
}
return `https://${provider.type}`;
// if (provider.type === "WeChat") {
// return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
// } else if (provider.type === "GitHub") {
// return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
// }
}
buyProduct(product, provider) {
this.setState({
isPlacingOrder: true,
@@ -176,12 +138,11 @@ class ProductBuyPage extends React.Component {
if (product.state !== "Published") {
return i18next.t("product:This product is currently not in sale.");
}
if (product.providers.length === 0) {
if (product.providerObjs.length === 0) {
return i18next.t("product:There is no payment channel for this product.");
}
const providers = this.getProviders(product);
return providers.map(provider => {
return product.providerObjs.map(provider => {
return this.renderProviderButton(provider, product);
});
}

View File

@@ -15,7 +15,6 @@
import React from "react";
import {Button, Card, Col, Input, Result, Row, Select, Spin, Switch} from "antd";
import * as UserBackend from "./backend/UserBackend";
import * as UserWebauthnBackend from "./backend/UserWebauthnBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting";
import {LinkOutlined} from "@ant-design/icons";

View File

@@ -22,7 +22,7 @@ export function getApplications(owner, page = "", pageSize = "", field = "", val
}
export function getApplicationsByOrganization(owner, organization) {
return fetch(`${Setting.ServerUrl}/api/get-applications?owner=${owner}&organization=${organization}`, {
return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());

View File

@@ -6,65 +6,65 @@
"Sign Up": "Регистрация"
},
"application": {
"Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Copy SAML metadata URL": "Копировать адрес метаданных SAML",
"Copy prompt page URL": "Скопировать URL-адрес страницы запроса",
"Copy signin page URL": "Скопировать URL-адрес страницы входа",
"Copy signup page URL": "Скопировать URL-адрес страницы регистрации",
"Edit Application": "Изменить приложение",
"Enable SAML compress": "Enable SAML compress",
"Enable SAML compress - Tooltip": "Enable SAML compress - Tooltip",
"Enable WebAuthn signin": "Enable WebAuthn signin",
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable SAML compress": "Включить сжатие SAML",
"Enable SAML compress - Tooltip": "Включить SAML сжатие - Подсказка",
"Enable WebAuthn signin": "Включить вход через WebAuthn",
"Enable WebAuthn signin - Tooltip": "Включить вход с WebAuthn - Подсказка",
"Enable code signin": "Включить кодовый вход",
"Enable code signin - Tooltip": "Включить вход с кодом - Tooltip",
"Enable signin session - Tooltip": "Включить сеанс входа - Подсказка",
"Enable signup": "Включить регистрацию",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "Файл успешно загружен",
"Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Grant types": "Виды грантов",
"Grant types - Tooltip": "Виды грантов - Подсказка",
"New Application": "Новое приложение",
"Password ON": "Пароль ВКЛ",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "Пожалуйста, выберите HTML-файл",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Ссылка на страницу успешно скопирована в буфер обмена, пожалуйста, вставьте ее в окно инкогнито или другой браузер",
"Redirect URL": "URL перенаправления",
"Redirect URLs": "Перенаправление URL",
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "Срок действия обновления токена истекает",
"Refresh token expire - Tooltip": "Срок обновления токена истекает - Подсказка",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"SAML metadata": "Метаданные SAML",
"SAML metadata - Tooltip": "Метаданные SAML - Подсказка",
"SAML metadata URL copied to clipboard successfully": "Адрес метаданных SAML скопирован в буфер обмена",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы входа успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер",
"Signin session": "Сессия входа",
"Signup items": "Элементы регистрации",
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы регистрации успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер",
"Token expire": "Токен истекает",
"Token expire - Tooltip": "Истек токен - Подсказка",
"Token format": "Формат токена",
"Token format - Tooltip": "Формат токена - Подсказка",
"rule": "rule"
"rule": "правило"
},
"cert": {
"Bit size": "Размер бита",
"Bit size - Tooltip": "Размер бита - Подсказка",
"Copy private key": "Копировать закрытый ключ",
"Copy certificate": "Копирование сертификата",
"Crypto algorithm": "Алгоритм крипто",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Crypto algorithm": "Алгоритм шифрования",
"Crypto algorithm - Tooltip": "Алгоритм шифрования - Подсказка",
"Download private key": "Скачать закрытый ключ",
"Download certificate": "Скачать сертификат",
"Edit Cert": "Изменить сертификат",
"Expire in years": "Истекает через годы",
"Expire in years - Tooltip": "Истекает через годы - Подсказка",
"New Cert": "New Cert",
"New Cert": "Новый сертификат",
"Private key": "Приватный ключ",
"Private key - Tooltip": "Приватный ключ - Подсказка",
"Private key copied to clipboard successfully": "Приватный ключ скопирован в буфер обмена",
"Certificate": "сертификат",
"Certificate - Tooltip": "сертификат - Подсказка",
"Certificate": "Сертификат",
"Certificate - Tooltip": "Сертификат - Подсказка",
"Certificate copied to clipboard successfully": "Сертификат успешно скопирован в буфер обмена",
"Scope": "Сфера охвата",
"Scope - Tooltip": "Область применения - Подсказка",
@@ -91,9 +91,9 @@
"Next Step": "Следующий шаг",
"Password": "Пароль",
"Please input your username!": "Пожалуйста, введите ваше имя пользователя!",
"Reset": "Reset",
"Reset": "Сбросить",
"Retrieve password": "Получить пароль",
"Unknown forget type": "Unknown forget type",
"Unknown forget type": "Неизвестный тип",
"Verify": "Подтвердить"
},
"general": {
@@ -107,12 +107,12 @@
"Avatar": "Аватар",
"Avatar - Tooltip": "Avatar to show to others",
"Back Home": "Назад",
"Cancel": "Cancel",
"Cancel": "Отмена",
"Captcha": "Капча",
"Cert": "Cert",
"Cert - Tooltip": "Cert - Tooltip",
"Cert": "Сертификат",
"Cert - Tooltip": "Сертификат - Подсказка",
"Certs": "Сертификаты",
"Click to Upload": "Click to Upload",
"Click to Upload": "Нажмите здесь, чтобы загрузить",
"Client IP": "IP клиента",
"Created time": "Время создания",
"Default avatar": "Аватар по умолчанию",
@@ -126,9 +126,9 @@
"Edit": "Редактирование",
"Email": "Почта",
"Email - Tooltip": "email",
"Favicon": "Favicon",
"Favicon": "Иконка",
"Favicon - Tooltip": "Application icon",
"First name": "First name",
"First name": "Имя",
"Forget URL": "Забыть URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "Домашний",
@@ -139,15 +139,15 @@
"Is enabled - Tooltip": "Включено - Подсказка",
"LDAPs": "LDAPы",
"LDAPs - Tooltip": "LDAPs - Подсказки",
"Last name": "Last name",
"Logo": "Logo",
"Last name": "Фамилия",
"Logo": "Логотип",
"Logo - Tooltip": "App's image tag",
"Master password": "Мастер-пароль",
"Master password - Tooltip": "Мастер-пароль - Tooltip",
"Method": "Метод",
"Model": "Model",
"Model - Tooltip": "Model - Tooltip",
"Models": "Models",
"Model": "Модель",
"Model - Tooltip": "Модель - Подсказка",
"Models": "Модели",
"Name": "Наименование",
"Name - Tooltip": "Unique string-style identifier",
"OAuth providers": "Поставщики OAuth",
@@ -160,7 +160,7 @@
"Password salt - Tooltip": "Random parameters used for password encryption",
"Password type": "Тип пароля",
"Password type - Tooltip": "The form in which the password is stored in the database",
"Payments": "Payments",
"Payments": "Платежи",
"Permissions": "Права доступа",
"Phone": "Телефон",
"Phone - Tooltip": "Phone",
@@ -168,9 +168,9 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Предпросмотр",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Products",
"Products": "Продукты",
"Provider": "Поставщик",
"Provider - Tooltip": "Provider - Tooltip",
"Provider - Tooltip": "Провайдер - Подсказка",
"Providers": "Поставщики",
"Providers - Tooltip": "List of third-party applications that can be used to log in",
"Real name": "Личное имя",
@@ -242,7 +242,7 @@
"Continue with": "Продолжить с",
"Email or phone": "Электронная почта или телефон",
"Forgot password?": "Забыли пароль?",
"Logging out...": "Logging out...",
"Logging out...": "Выход...",
"No account?": "Нет учетной записи?",
"Or sign in with another account": "Или войти с помощью другой учетной записи",
"Password": "Пароль",
@@ -306,7 +306,7 @@
"Invoice type": "Invoice type",
"Invoice type - Tooltip": "Invoice type - Tooltip",
"Issue Invoice": "Issue Invoice",
"Message": "Message",
"Message": "Сообщение",
"Message - Tooltip": "Message - Tooltip",
"New Payment": "New Payment",
"Organization": "Organization",
@@ -321,21 +321,21 @@
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
"Please pay the order first!": "Please pay the order first!",
"Price": "Price",
"Price": "Стоимость",
"Price - Tooltip": "Price - Tooltip",
"Processing...": "Processing...",
"Processing...": "Обработка...",
"Product": "Product",
"Product - Tooltip": "Product - Tooltip",
"Result": "Result",
"Return to Website": "Return to Website",
"State": "State",
"Return to Website": "Вернуться на сайт",
"State": "Регион",
"State - Tooltip": "State - Tooltip",
"The payment has failed": "The payment has failed",
"The payment is still under processing": "The payment is still under processing",
"Type": "Type",
"The payment has failed": "Платеж не удался",
"The payment is still under processing": "Платеж еще обрабатывается",
"Type": "Тип",
"Type - Tooltip": "Type - Tooltip",
"You have successfully completed the payment": "You have successfully completed the payment",
"please wait for a few seconds...": "please wait for a few seconds...",
"please wait for a few seconds...": "пожалуйста, подождите несколько секунд...",
"the current state is": "the current state is"
},
"permission": {
@@ -352,36 +352,36 @@
},
"product": {
"Alipay": "Alipay",
"Buy": "Buy",
"Buy Product": "Buy Product",
"Buy": "Купить",
"Buy Product": "Купить товар",
"CNY": "CNY",
"Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail": "Сведения",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product",
"Image": "Image",
"Edit Product": "Редактирование продукта",
"Image": "Изображение",
"Image - Tooltip": "Image - Tooltip",
"New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers",
"New Product": "Новый продукт",
"Pay": "Оплатить",
"Payment providers": "Платежные системы",
"Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Paypal": "PayPal",
"Placing order...": "Placing order...",
"Price": "Price",
"Price": "Цена",
"Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip",
"Return URL": "Return URL",
"Return URL - Tooltip": "Return URL - Tooltip",
"SKU": "SKU",
"Sold": "Sold",
"Sold": "Продано",
"Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"This product is currently not in sale.": "В данное время этот продукт не продается.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
},

View File

@@ -4907,6 +4907,18 @@ eslint-plugin-testing-library@^3.9.2:
dependencies:
"@typescript-eslint/experimental-utils" "^3.10.1"
eslint-plugin-unused-imports@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz#d8db8c4d0cfa0637a8b51ce3fd7d1b6bc3f08520"
integrity sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==
dependencies:
eslint-rule-composer "^0.3.0"
eslint-rule-composer@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
eslint-scope@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"