Compare commits

...

38 Commits

Author SHA1 Message Date
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
8e5cd18c91 fix: Restrict the request permissions of providers and applications (#970) 2022-08-07 16:05:05 +08:00
32b4d98c2a Add ExtendProductWithProviders(). 2022-08-07 15:45:06 +08:00
2ea58cd639 chore(style): use gofumpt to fmt go code (#967) 2022-08-07 12:26:14 +08:00
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
cba338eef2 Merge pull request #973 from qianxi0410/eslint
feat(web): add some eslint rules
2022-08-07 00:41:51 +08:00
c428de6e42 feat: fix some comma dangle 2022-08-07 00:17:27 +08:00
9bca6bb72e feat: no-multi-spacing 2022-08-07 00:06:20 +08:00
cd966116d4 feat: comma dangle 2022-08-06 23:54:56 +08:00
9abf1b9d73 feat: key spacing 2022-08-06 23:47:28 +08:00
6aaba6debd feat: space between infix op 2022-08-06 23:43:09 +08:00
77565712e0 feat: no-multi-empty-lines 2022-08-06 23:38:03 +08:00
d025259db7 feat: indent 2022-08-06 23:36:20 +08:00
aafdc546fa fix: panic when creating a user in a non-existent org (#969) 2022-08-06 22:30:56 +08:00
539ca2d731 chore(web): add fix command (#964) 2022-08-05 23:40:04 +08:00
ea326b3513 fix: show social buttons on signup page (#962) 2022-08-05 18:59:56 +08:00
98ef766fb4 fix: fix webauthn entry cannot add bug (#960)
* fix: fix webauthn

* Update LoginPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-05 17:43:04 +08:00
e94ada9ea2 Fix new accountItem. 2022-08-05 15:36:07 +08:00
4ea482223d feat: add geetest captcha (#953) 2022-08-04 20:55:04 +08:00
d55ae7d1d2 Enable some other DBs 2022-08-04 20:28:09 +08:00
d72e00605f fix: updateProviderField when add provider payment (#952) 2022-08-04 19:39:25 +08:00
be74cb621f feat: Support sub-directory (#943)
By adding PUBLIC_URL to relative `.`

Signed-off-by: zzjin <tczzjin@gmail.com>
2022-08-02 00:21:15 +08:00
13404d6035 feat: fix binding after registration causes the page to crash (#945) 2022-08-01 21:08:10 +08:00
afa9c530ad fix: panic triggered when user is nil (#940) 2022-07-31 23:23:36 +08:00
1600615aca Support sqlite3 DB 2022-07-31 18:11:18 +08:00
2bb8491499 fix: unable to get user if profile is private (#936) 2022-07-31 10:54:41 +08:00
293283ed25 feat: add get user by phone (#934)
* fix: check reset phone & email modify rules

* Update verification.go

* Update organization.go

* feat: add get user by phone

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-07-31 01:02:28 +08:00
9cb519d1e9 fix: Admins should not be allowed to add third-party login for their members (#932)
* feat: admin can unlink the other user

* feat: global admin can unlink other user

* fix
2022-07-30 23:11:02 +08:00
fb9b8f1662 fix: skip the duplicated users when sync users (#928)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-07-30 22:24:23 +08:00
2fec3f72ae fix: check reset phone & email modify rules (#927)
* fix: check reset phone & email modify rules

* Update verification.go

* Update organization.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-07-30 18:17:13 +08:00
11695220a8 Use user.GetId() 2022-07-30 17:40:30 +08:00
155660b0d7 feat: get user api return roles and permissions (#929) 2022-07-30 17:31:56 +08:00
1c72f5300c feat: fix 'Enable code sign' is not displayed in the login page (#925) 2022-07-28 23:11:33 +08:00
3dd56195d9 fix: fix the problem of link error (#923) 2022-07-28 21:52:10 +08:00
8865244262 fix: add oauth login auto close page (#915) 2022-07-26 23:03:55 +08:00
3400fa1e9c feat: support local login for non-built-in users (#911)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-07-26 19:27:24 +08:00
bdc5c92ef0 fix: send code missing parameter & show more detail responseError (#910) 2022-07-25 23:46:38 +08:00
4e3eedf246 feat: fix bug that the default permission prevents admin to login in (#907)
* fix:The certs page is displayed incorrectly

* Translations for each language are added

* Replace the variables certificat with Certificat with certificate and Certificate

* Replace the variables certificat with Certificat with certificate and Certificate

* Variable names are more accurate

* Variable names are more accurate

* Modify the variable name

* fix: Default action prevents admin to login in
2022-07-24 23:36:55 +08:00
167 changed files with 1416 additions and 831 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{}

81
captcha/geetest.go Normal file
View File

@ -0,0 +1,81 @@
// 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)
}

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

@ -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
}

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

@ -217,7 +217,7 @@ func (c *ApiController) Signup() {
record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) })
userId := fmt.Sprintf("%s/%s", user.Owner, user.Name)
userId := user.GetId()
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
c.ResponseOk(userId)

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

@ -21,7 +21,8 @@ import (
)
type LinkForm struct {
ProviderType string `json:"providerType"`
ProviderType string `json:"providerType"`
User object.User `json:"user"`
}
// Unlink ...
@ -40,16 +41,55 @@ func (c *ApiController) Unlink() {
}
providerType := form.ProviderType
// the user will be unlinked from the provider
unlinkedUser := form.User
user := object.GetUser(userId)
value := object.GetUserField(user, providerType)
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError("You are not the global admin, you can't unlink other users")
return
}
if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application := object.GetApplicationByUser(user)
if application == nil {
c.ResponseError("You can't unlink yourself, you are not a member of any application")
return
}
if len(application.Providers) == 0 {
c.ResponseError("This application has no providers")
return
}
provider := application.GetProviderItemByType(providerType)
if provider == nil {
c.ResponseError("This application has no providers of type " + providerType)
return
}
if !provider.CanUnlink {
c.ResponseError("This provider can't be unlinked")
return
}
}
// only two situations can happen here
// 1. the user is the global admin
// 2. the user is unlinking themselves and provider can be unlinked
value := object.GetUserField(&unlinkedUser, providerType)
if value == "" {
c.ResponseError("Please link first", value)
return
}
object.ClearUserOAuthProperties(user, providerType)
object.ClearUserOAuthProperties(&unlinkedUser, providerType)
object.LinkUserAccount(user, providerType, "")
object.LinkUserAccount(&unlinkedUser, providerType, "")
c.ResponseOk()
}

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

@ -80,12 +80,16 @@ func (c *ApiController) GetUsers() {
// @Title GetUser
// @Tag User API
// @Description get user
// @Param id query string true "The id of the user"
// @Param id query string true "The id of the user"
// @Param owner query string false "The owner of the user"
// @Param email query string false "The email of the user"
// @Param phone query string false "The phone of the user"
// @Success 200 {object} object.User The Response object
// @router /get-user [get]
func (c *ApiController) GetUser() {
id := c.Input().Get("id")
email := c.Input().Get("email")
phone := c.Input().Get("phone")
userId := c.Input().Get("userId")
owner := c.Input().Get("owner")
@ -96,7 +100,7 @@ func (c *ApiController) GetUser() {
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner))
if !organization.IsProfilePublic {
requestUserId := c.GetSessionUsername()
hasPermission, err := object.CheckUserPermission(requestUserId, id, false)
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false)
if !hasPermission {
c.ResponseError(err.Error())
return
@ -104,14 +108,24 @@ func (c *ApiController) GetUser() {
}
var user *object.User
if email != "" {
switch {
case email != "":
user = object.GetUserByEmail(owner, email)
} else if userId != "" {
case phone != "":
user = object.GetUserByPhone(owner, phone)
case userId != "":
user = object.GetUserByUserId(owner, userId)
} else {
default:
user = object.GetUser(id)
}
if user != nil {
roles := object.GetRolesByUser(user.GetId())
user.Roles = roles
permissions := object.GetPermissionsByUser(user.GetId())
user.Permissions = permissions
}
c.Data["json"] = object.GetMaskedUser(user)
c.ServeJSON()
}
@ -252,7 +266,7 @@ func (c *ApiController) SetPassword() {
requestUserId := c.GetSessionUsername()
userId := fmt.Sprintf("%s/%s", userOwner, userName)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true)
if !hasPermission {
c.ResponseError(err.Error())
return

View File

@ -49,8 +49,24 @@ func (c *ApiController) SendVerificationCode() {
applicationId := c.Ctx.Request.Form.Get("applicationId")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" || dest == "" || applicationId == "" || !strings.Contains(applicationId, "/") || checkType == "" {
c.ResponseError("Missing parameter.")
if destType == "" {
c.ResponseError("Missing parameter: type.")
return
}
if dest == "" {
c.ResponseError("Missing parameter: dest.")
return
}
if applicationId == "" {
c.ResponseError("Missing parameter: applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError("Wrong parameter: applicationId.")
return
}
if checkType == "" {
c.ResponseError("Missing parameter: checkType.")
return
}
@ -152,13 +168,35 @@ func (c *ApiController) ResetEmailOrPhone() {
}
checkDest := dest
org := object.GetOrganizationByUser(user)
if destType == "phone" {
org := object.GetOrganizationByUser(user)
phoneItem := object.GetAccountItemByName("Phone", org)
if phoneItem == nil {
c.ResponseError("Unable to get the phone modify rule.")
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user); !pass {
c.ResponseError(errMsg)
return
}
phonePrefix := "86"
if org != nil && org.PhonePrefix != "" {
phonePrefix = org.PhonePrefix
}
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org)
if emailItem == nil {
c.ResponseError("Unable to get the email modify rule.")
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user); !pass {
c.ResponseError(errMsg)
return
}
}
if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 {
c.ResponseError(ret)

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)
}
}

2
go.mod
View File

@ -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
View File

@ -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=

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

@ -21,9 +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/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"
)

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)
@ -197,14 +197,18 @@ func filterField(field string) bool {
return reFieldWhiteList.MatchString(field)
}
func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error) {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf("please login first")
}
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId)
if userId != "" {
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId)
}
userOwner = targetUser.Owner
}
hasPermission := false
@ -219,7 +223,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error
hasPermission = true
} else if requestUserId == userId {
hasPermission = true
} else if targetUser.Owner == requestUser.Owner {
} else if userOwner == requestUser.Owner {
if strict {
hasPermission = requestUser.IsAdmin
} else {
@ -236,7 +240,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
allowed := true
var err error
for _, permission := range permissions {
if !permission.IsEnabled {
if !permission.IsEnabled || len(permission.Users) == 0 {
continue
}

View File

@ -25,6 +25,7 @@ import (
func InitDb() {
existed := initBuiltInOrganization()
if !existed {
initBuiltInPermission()
initBuiltInProvider()
initBuiltInUser()
initBuiltInApplication()
@ -70,6 +71,8 @@ func initBuiltInOrganization() bool {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@ -230,3 +233,25 @@ func initBuiltInProvider() {
func initWebAuthn() {
gob.Register(webauthn.SessionData{})
}
func initBuiltInPermission() {
permission := GetPermission("built-in/permission-built-in")
if permission != nil {
return
}
permission = &Permission{
Owner: "built-in",
Name: "permission-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Permission",
Users: []string{"built-in/admin"},
Roles: []string{},
ResourceType: "Application",
Resources: []string{"app-built-in"},
Actions: []string{"Read", "Write", "Admin"},
Effect: "Allow",
IsEnabled: true,
}
AddPermission(permission)
}

View File

@ -89,6 +89,8 @@ func initDefinedOrganization(organization *Organization) {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},

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

@ -15,6 +15,8 @@
package object
import (
"fmt"
"github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -186,3 +188,31 @@ func DeleteOrganization(organization *Organization) bool {
func GetOrganizationByUser(user *User) *Organization {
return getOrganization("admin", user.Owner)
}
func GetAccountItemByName(name string, organization *Organization) *AccountItem {
if organization == nil {
return nil
}
for _, accountItem := range organization.AccountItems {
if accountItem.Name == name {
return accountItem
}
}
return nil
}
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, string) {
switch accountItem.ModifyRule {
case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) {
return false, fmt.Sprintf("Only admin can modify the %s.", accountItem.Name)
}
case "Immutable":
return false, fmt.Sprintf("The %s is immutable.", accountItem.Name)
case "Self":
break
default:
return false, fmt.Sprintf("Unknown modify rule %s.", accountItem.ModifyRule)
}
return true, ""
}

View File

@ -229,3 +229,13 @@ func removePolicies(permission *Permission) {
panic(err)
}
}
func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
if err != nil {
panic(err)
}
return permissions
}

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

@ -33,6 +33,15 @@ func (application *Application) GetProviderItem(providerName string) *ProviderIt
return nil
}
func (application *Application) GetProviderItemByType(providerType string) *ProviderItem {
for _, item := range application.Providers {
if item.Provider.Type == providerType {
return item
}
}
return nil
}
func (pi *ProviderItem) IsProviderVisible() bool {
if pi.Provider == nil {
return false

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

@ -121,3 +121,13 @@ func DeleteRole(role *Role) bool {
func (role *Role) GetId() string {
return fmt.Sprintf("%s/%s", role.Owner, role.Name)
}
func GetRolesByUser(userId string) []*Role {
roles := []*Role{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&roles)
if err != nil {
panic(err)
}
return roles
}

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

@ -22,7 +22,7 @@ import (
func (syncer *Syncer) syncUsers() {
fmt.Printf("Running syncUsers()..\n")
users, userMap := syncer.getUserMap()
users, userMap, userNameMap := syncer.getUserMap()
oUsers, oUserMap, err := syncer.getOriginalUserMap()
if err != nil {
fmt.Printf(err.Error())
@ -44,9 +44,11 @@ func (syncer *Syncer) syncUsers() {
for _, oUser := range oUsers {
id := oUser.Id
if _, ok := userMap[id]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
if _, ok := userNameMap[oUser.Name]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
}
} else {
user := userMap[id]
oHash := syncer.calculateHash(oUser)

View File

@ -19,12 +19,15 @@ func (syncer *Syncer) getUsers() []*User {
return users
}
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User) {
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User, map[string]*User) {
users := syncer.getUsers()
m := map[string]*User{}
m1 := map[string]*User{}
m2 := map[string]*User{}
for _, user := range users {
m[user.Id] = user
m1[user.Id] = user
m2[user.Name] = user
}
return users, m
return users, m1, m2
}

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

@ -73,7 +73,7 @@ type User struct {
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
Github string `xorm:"varchar(100)" json:"github"`
GitHub string `xorm:"github varchar(100)" json:"github"`
Google string `xorm:"varchar(100)" json:"google"`
QQ string `xorm:"qq varchar(100)" json:"qq"`
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
@ -104,6 +104,9 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
}
type Userinfo struct {
@ -270,6 +273,24 @@ func GetUserByEmail(owner string, email string) *User {
}
}
func GetUserByPhone(owner string, phone string) *User {
if owner == "" || phone == "" {
return nil
}
user := User{Owner: owner, Phone: phone}
existed, err := adapter.Engine.Get(&user)
if err != nil {
panic(err)
}
if existed {
return &user
} else {
return nil
}
}
func GetUserByUserId(owner string, userId string) *User {
if owner == "" || userId == "" {
return nil
@ -396,6 +417,10 @@ func AddUser(user *User) bool {
}
organization := GetOrganizationByUser(user)
if organization == nil {
return false
}
user.UpdateUserPassword(organization)
user.UpdateUserHash()

View File

@ -37,11 +37,11 @@ func TestSyncAvatarsFromGitHub(t *testing.T) {
users := GetGlobalUsers()
for _, user := range users {
if user.Github == "" {
if user.GitHub == "" {
continue
}
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.Github)
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.GitHub)
updateUserColumn("avatar", user)
}
}

View File

@ -106,6 +106,10 @@ func setUserProperty(user *User, field string, value string) {
if value == "" {
delete(user.Properties, field)
} else {
if user.Properties == nil {
user.Properties = make(map[string]string)
}
user.Properties[field] = value
}
}

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

@ -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))
}

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

1
web/.env Normal file
View File

@ -0,0 +1 @@
PUBLIC_URL=.

View File

@ -17,11 +17,12 @@
"version": "detect"
}
},
"plugins": ["unused-imports"],
"extends": ["eslint:recommended", "plugin:react/recommended"],
"rules": {
// "eqeqeq": "error",
"semi": ["error", "always"],
// "indent": ["error", 2],
"indent": ["error", 2],
// follow antd's style guide
"quotes": ["error", "double"],
"jsx-quotes": ["error", "prefer-double"],
@ -45,17 +46,48 @@
"curly": ["error", "all"],
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"no-mixed-spaces-and-tabs": "error",
"sort-imports": ["error", {
"ignoreDeclarationSort": true
}],
"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"
}
],
"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

@ -34,7 +34,7 @@ module.exports = {
"/cas/validate": {
target: "http://localhost:8000",
changeOrigin: true,
}
},
},
},
plugins: [

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",
@ -39,7 +40,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"

View File

@ -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 = [];
}
@ -86,6 +86,8 @@ class AccountTable extends React.Component {
{name: "Bio", displayName: i18next.t("user:Bio")},
{name: "Tag", displayName: i18next.t("user:Tag")},
{name: "Signup application", displayName: i18next.t("general:Signup application")},
{name: "Roles", displayName: i18next.t("general:Roles")},
{name: "Permissions", displayName: i18next.t("general:Permissions")},
{name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")},
{name: "Properties", displayName: i18next.t("user:Properties")},
{name: "Is admin", displayName: i18next.t("user:Is admin")},
@ -114,7 +116,7 @@ class AccountTable extends React.Component {
}
</Select>
);
}
},
},
{
title: i18next.t("provider:visible"),
@ -127,7 +129,7 @@ class AccountTable extends React.Component {
this.updateField(table, index, "visible", checked);
}} />
);
}
},
},
{
title: i18next.t("organization:viewRule"),
@ -154,7 +156,7 @@ class AccountTable extends React.Component {
}
</Select>
);
}
},
},
{
title: i18next.t("organization:modifyRule"),
@ -189,7 +191,7 @@ class AccountTable extends React.Component {
}
</Select>
);
}
},
},
{
title: i18next.t("general:Action"),
@ -209,7 +211,7 @@ class AccountTable extends React.Component {
</Tooltip>
</div>
);
}
},
},
];

View File

@ -235,15 +235,19 @@ class App extends Component {
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = this.state.account.owner;
this.setState({
account: null
account: null,
});
Setting.showMessage("success", "Logged out successfully");
let redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
}else{
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft(this, "/");
}
} else {
@ -254,7 +258,7 @@ class App extends Component {
onUpdateAccount(account) {
this.setState({
account: account
account: account,
});
}
@ -562,7 +566,7 @@ class App extends Component {
renderContent() {
if (!Setting.isMobile()) {
return (
<div style={{display: "flex", flex: "auto", width:"100%", flexDirection: "column"}}>
<div style={{display: "flex", flex: "auto", width: "100%", flexDirection: "column"}}>
<Layout style={{display: "flex", alignItems: "stretch"}}>
<Header style={{padding: "0", marginBottom: "3px"}}>
{
@ -669,6 +673,7 @@ class App extends Component {
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} />
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} />
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />

View File

@ -156,7 +156,7 @@ class ApplicationEditPage extends React.Component {
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitApplicationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteApplication()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
} style={(Setting.isMobile())? {margin: "5px"}:{}} type="inner">
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
@ -181,7 +181,7 @@ class ApplicationEditPage extends React.Component {
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
</Col>
<Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} :{}}>
<Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
{Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
@ -583,7 +583,7 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signup page URL")}
</Button>
<br />
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems:"center", overflow:"auto", flexDirection:"column", flex: "auto"}}>
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
{
this.state.application.enablePassword ? (
<SignupPage application={this.state.application} />
@ -603,7 +603,7 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signin page URL")}
</Button>
<br />
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems:"center", overflow:"auto", flexDirection:"column", flex: "auto"}}>
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} />
<div style={maskStyle}></div>
</div>

View File

@ -99,7 +99,7 @@ class ApplicationListPage extends BaseListPage {
{text}
</Link>
);
}
},
},
{
title: i18next.t("general:Created time"),
@ -109,7 +109,7 @@ class ApplicationListPage extends BaseListPage {
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
}
},
},
{
title: i18next.t("general:Display name"),
@ -130,7 +130,7 @@ class ApplicationListPage extends BaseListPage {
<img src={text} alt={text} width={150} />
</a>
);
}
},
},
{
title: i18next.t("general:Organization"),
@ -145,7 +145,7 @@ class ApplicationListPage extends BaseListPage {
{text}
</Link>
);
}
},
},
{
title: i18next.t("general:Providers"),
@ -222,7 +222,7 @@ class ApplicationListPage extends BaseListPage {
</Popconfirm>
</div>
);
}
},
},
];

View File

@ -73,7 +73,7 @@ class CertEditPage extends React.Component {
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitCertEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteCert()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
} style={(Setting.isMobile())? {margin: "5px"}:{}} type="inner">
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :

View File

@ -82,7 +82,7 @@ class CertListPage extends BaseListPage {
{text}
</Link>
);
}
},
},
{
title: i18next.t("general:Created time"),
@ -92,7 +92,7 @@ class CertListPage extends BaseListPage {
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
}
},
},
{
title: i18next.t("general:Display name"),
@ -169,7 +169,7 @@ class CertListPage extends BaseListPage {
</Popconfirm>
</div>
);
}
},
},
];

View File

@ -42,7 +42,7 @@ class LdapEditPage extends React.Component {
.then((res) => {
if (res.status === "ok") {
this.setState({
ldap: res.data
ldap: res.data,
});
} else {
Setting.showMessage("error", res.msg);
@ -71,7 +71,7 @@ class LdapEditPage extends React.Component {
return (
<span style={{
color: "#faad14",
marginLeft: "20px"
marginLeft: "20px",
}}>{i18next.t("ldap:The Auto Sync option will sync all users to specify organization")}</span>
);
}

View File

@ -23,7 +23,7 @@ class LdapListPage extends React.Component {
constructor(props) {
super(props);
this.state = {
ldaps: null
ldaps: null,
};
}
@ -65,7 +65,7 @@ class LdapListPage extends React.Component {
{text}
</Link>
);
}
},
},
{
title: i18next.t("general:Organization"),
@ -79,7 +79,7 @@ class LdapListPage extends React.Component {
{text}
</Link>
);
}
},
},
{
title: i18next.t("ldap:Server"),
@ -89,7 +89,7 @@ class LdapListPage extends React.Component {
sorter: (a, b) => a.host.localeCompare(b.host),
render: (text, record, index) => {
return `${text}:${record.port}`;
}
},
},
{
title: i18next.t("ldap:Base DN"),
@ -114,7 +114,7 @@ class LdapListPage extends React.Component {
render: (text, record, index) => {
return text === 0 ? (<span style={{color: "#faad14"}}>Disable</span>) : (
<span style={{color: "#52c41a"}}>{text + " mins"}</span>);
}
},
},
{
title: i18next.t("ldap:Last Sync"),
@ -124,7 +124,7 @@ class LdapListPage extends React.Component {
sorter: (a, b) => a.lastSync.localeCompare(b.lastSync),
render: (text, record, index) => {
return text;
}
},
},
{
title: i18next.t("general:Action"),
@ -148,7 +148,7 @@ class LdapListPage extends React.Component {
</Popconfirm>
</div>
);
}
},
},
];

Some files were not shown because too many files have changed in this diff Show More