Compare commits

...

14 Commits

Author SHA1 Message Date
Yaodong Yu
6455734807 fix: fix incorrect LDAP sync status (#1859) 2023-05-18 22:03:53 +08:00
Trần Thanh Tịnh
2eefeaffa7 feat: enforce by using resourceId (#1855)
* feat: enforce by using resourceId

* Update permission.go

* chore: fix cilint for enforcer.go

---------

Co-authored-by: tinhtt4 <tinhtt4@vng.com.vn>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-05-18 16:36:03 +08:00
Yang Luo
04eaad1c80 Fix getCertByApplication() 2023-05-18 16:32:43 +08:00
Yang Luo
9f084a0799 Can update user with OAuth values 2023-05-18 15:58:41 +08:00
Yang Luo
293b9f1036 Remove languages in app.conf 2023-05-18 15:44:11 +08:00
Yang Luo
437376c472 Fix CheckAccessPermission() 2023-05-18 13:36:16 +08:00
Yang Luo
cc528c5d8c Add object to webhook 2023-05-17 23:57:14 +08:00
Yang Luo
54e2055ffb Fix Beego filter: RecordMessage 2023-05-17 23:01:59 +08:00
Yang Luo
983a30a2e0 Dingtalk now supports linking with corpMobile 2023-05-17 22:14:57 +08:00
Yang Luo
37d0157d41 Fix application.EnableSignUp bug 2023-05-17 21:56:36 +08:00
Yang Luo
d4dc236770 Fix refreshExpireInHours zero value issue 2023-05-17 20:47:59 +08:00
Yang Luo
596742d782 Show org column better for admin (shared) 2023-05-17 17:30:47 +08:00
XDTD
ce921c00cd fix: resolve the problem of cert being unable to be accessed properly (#1850)
* fix: resolve the problem of cert being unable to be accessed properly

* Update CertEditPage.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-05-17 17:17:58 +08:00
Yang Luo
3830e443b0 Put webhook's RecordMessage() to FinishRouter stage 2023-05-17 16:32:12 +08:00
26 changed files with 228 additions and 116 deletions

View File

@@ -20,5 +20,4 @@ staticBaseUrl = "https://cdn.casbin.org"
isDemoMode = false
batchSize = 100
ldapServerPort = 389
languages = en,zh,es,fr,de,id,ja,ko,ru,vi
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}

View File

@@ -112,13 +112,8 @@ func GetLanguage(language string) string {
if len(language) < 2 {
return "en"
}
language = language[0:2]
if strings.Contains(GetConfigString("languages"), language) {
return language
} else {
return "en"
return language[0:2]
}
}

View File

@@ -416,22 +416,29 @@ func (c *ApiController) Login() {
util.SafeGoroutine(func() { object.AddRecord(record) })
} else if provider.Category == "OAuth" {
// Sign up via OAuth
if !application.EnableSignUp {
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support"), provider.Type, userInfo.Username, userInfo.DisplayName))
return
}
if !providerItem.CanSignUp {
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
return
}
if application.EnableLinkWithEmail {
// find user that has the same email
user = object.GetUserByField(application.Organization, "email", userInfo.Email)
if userInfo.Email != "" {
// Find existing user with Email
user = object.GetUserByField(application.Organization, "email", userInfo.Email)
}
if user == nil && userInfo.Phone != "" {
// Find existing user with phone number
user = object.GetUserByField(application.Organization, "phone", userInfo.Phone)
}
}
if user == nil || user.IsDeleted {
if !application.EnableSignUp {
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support"), provider.Type, userInfo.Username, userInfo.DisplayName))
return
}
if !providerItem.CanSignUp {
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
return
}
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {

View File

@@ -24,6 +24,7 @@ import (
func (c *ApiController) Enforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
resourceId := c.Input().Get("resourceId")
var request object.CasbinRequest
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
@@ -35,17 +36,24 @@ func (c *ApiController) Enforce() {
if permissionId != "" {
c.Data["json"] = object.Enforce(permissionId, &request)
c.ServeJSON()
} else {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions := object.GetPermissionsByModel(owner, modelName)
res := []bool{}
for _, permission := range permissions {
res = append(res, object.Enforce(permission.GetId(), &request))
}
c.Data["json"] = res
c.ServeJSON()
return
}
permissions := make([]*object.Permission, 0)
res := []bool{}
if modelId != "" {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions = object.GetPermissionsByModel(owner, modelName)
} else {
permissions = object.GetPermissionsByResource(resourceId)
}
for _, permission := range permissions {
res = append(res, object.Enforce(permission.GetId(), &request))
}
c.Data["json"] = res
c.ServeJSON()
}
func (c *ApiController) BatchEnforce() {

View File

@@ -86,7 +86,10 @@ func (c *ApiController) GetLdapUsers() {
Phone: util.GetMaxLenStr(user.TelephoneNumber, user.Mobile, user.MobileTelephoneNumber),
Address: util.GetMaxLenStr(user.RegisteredAddress, user.PostalAddress),
})
uuids = append(uuids, user.Uuid)
if user.Uuid != "" {
uuids = append(uuids, user.Uuid)
}
}
existUuids := object.GetExistUuids(ldapServer.Owner, uuids)
@@ -215,10 +218,10 @@ func (c *ApiController) SyncLdapUsers() {
object.UpdateLdapSyncTime(ldapId)
exist, failed := object.SyncLdapUsers(owner, users, ldapId)
exist, failed, _ := object.SyncLdapUsers(owner, users, ldapId)
c.ResponseOk(&LdapSyncResp{
Exist: *exist,
Failed: *failed,
Exist: exist,
Failed: failed,
})
}

View File

@@ -73,23 +73,27 @@ func applyData(data1 *I18nData, data2 *I18nData) {
}
}
func Translate(lang string, error string) string {
tokens := strings.SplitN(error, ":", 2)
if !strings.Contains(error, ":") || len(tokens) != 2 {
return "Translate Error: " + error
func Translate(language string, errorText string) string {
tokens := strings.SplitN(errorText, ":", 2)
if !strings.Contains(errorText, ":") || len(tokens) != 2 {
return fmt.Sprintf("Translate error: the error text doesn't contain \":\", errorText = %s", errorText)
}
if langMap[lang] == nil {
file, _ := f.ReadFile("locales/" + lang + "/data.json")
if langMap[language] == nil {
file, err := f.ReadFile(fmt.Sprintf("locales/%s/data.json", language))
if err != nil {
return fmt.Sprintf("Translate error: the language \"%s\" is not supported, err = %s", language, err.Error())
}
data := I18nData{}
err := util.JsonToStruct(string(file), &data)
err = util.JsonToStruct(string(file), &data)
if err != nil {
panic(err)
}
langMap[lang] = data
langMap[language] = data
}
res := langMap[lang][tokens[0]][tokens[1]]
res := langMap[language][tokens[0]][tokens[1]]
if res == "" {
res = tokens[1]
}

View File

@@ -179,8 +179,12 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
return nil, err
}
corpEmail, jobNumber, err := idp.getUserCorpEmail(userId, corpAccessToken)
corpMobile, corpEmail, jobNumber, err := idp.getUserCorpEmail(userId, corpAccessToken)
if err == nil {
if corpMobile != "" {
userInfo.Phone = corpMobile
}
if corpEmail != "" {
userInfo.Email = corpEmail
}
@@ -264,27 +268,29 @@ func (idp *DingTalkIdProvider) getUserId(unionId string, accessToken string) (st
return data.Result.UserId, nil
}
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, string, error) {
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, string, string, error) {
// https://open.dingtalk.com/document/isvapp/query-user-details
body := make(map[string]string)
body["userid"] = userId
respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/v2/user/get?access_token="+accessToken)
if err != nil {
return "", "", err
return "", "", "", err
}
var data struct {
ErrMessage string `json:"errmsg"`
Result struct {
Mobile string `json:"mobile"`
Email string `json:"email"`
JobNumber string `json:"job_number"`
} `json:"result"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
return "", "", err
return "", "", "", err
}
if data.ErrMessage != "ok" {
return "", "", fmt.Errorf(data.ErrMessage)
return "", "", "", fmt.Errorf(data.ErrMessage)
}
return data.Result.Email, data.Result.JobNumber, nil
return data.Result.Mobile, data.Result.Email, data.Result.JobNumber, nil
}

View File

@@ -59,8 +59,8 @@ func main() {
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
beego.BConfig.WebConfig.Session.SessionOn = true
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"

View File

@@ -134,6 +134,24 @@ func getCert(owner string, name string) *Cert {
}
}
func getCertByName(name string) *Cert {
if name == "" {
return nil
}
cert := Cert{Name: name}
existed, err := adapter.Engine.Get(&cert)
if err != nil {
panic(err)
}
if existed {
return &cert
} else {
return nil
}
}
func GetCert(id string) *Cert {
owner, name := util.GetOwnerAndNameFromId(id)
return getCert(owner, name)
@@ -189,7 +207,7 @@ func (p *Cert) GetId() string {
func getCertByApplication(application *Application) *Cert {
if application.Cert != "" {
return getCert("admin", application.Cert)
return getCertByName(application.Cert)
} else {
return GetDefaultCert()
}

View File

@@ -321,6 +321,10 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
}
func CheckAccessPermission(userId string, application *Application) (bool, error) {
if userId == "built-in/admin" {
return true, nil
}
permissions := GetPermissions(application.Organization)
allowed := true
var err error

View File

@@ -87,11 +87,13 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
continue
}
existed, failed := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users), ldap.Id)
if len(*failed) != 0 {
logs.Warning(fmt.Sprintf("ldap autosync,%d new users,but %d user failed during :", len(users)-len(*existed)-len(*failed), len(*failed)), *failed)
existed, failed, err := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users), ldap.Id)
if len(failed) != 0 {
logs.Warning(fmt.Sprintf("ldap autosync,%d new users,but %d user failed during :", len(users)-len(existed)-len(failed), len(failed)), failed)
logs.Warning(err.Error())
} else {
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(*existed), len(*existed)))
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(existed), len(existed)))
}
}
}

View File

@@ -259,17 +259,13 @@ func LdapUsersToLdapRespUsers(users []ldapUser) []LdapRespUser {
return res
}
func SyncLdapUsers(owner string, respUsers []LdapRespUser, ldapId string) (*[]LdapRespUser, *[]LdapRespUser) {
var existUsers []LdapRespUser
var failedUsers []LdapRespUser
func SyncLdapUsers(owner string, syncUsers []LdapRespUser, ldapId string) (existUsers []LdapRespUser, failedUsers []LdapRespUser, err error) {
var uuids []string
for _, user := range respUsers {
for _, user := range syncUsers {
uuids = append(uuids, user.Uuid)
}
existUuids := GetExistUuids(owner, uuids)
organization := getOrganization("admin", owner)
ldap := GetLdap(ldapId)
@@ -289,12 +285,19 @@ func SyncLdapUsers(owner string, respUsers []LdapRespUser, ldapId string) (*[]Ld
}
tag := strings.Join(ou, ".")
for _, respUser := range respUsers {
for _, syncUser := range syncUsers {
if syncUser.Uuid == "" {
failedUsers = append(failedUsers, syncUser)
err = errors.New("uuid of user being synced is empty")
continue
}
existUuids := GetExistUuids(owner, uuids)
found := false
if len(existUuids) > 0 {
for _, existUuid := range existUuids {
if respUser.Uuid == existUuid {
existUsers = append(existUsers, respUser)
if syncUser.Uuid == existUuid {
existUsers = append(existUsers, syncUser)
found = true
}
}
@@ -303,49 +306,39 @@ func SyncLdapUsers(owner string, respUsers []LdapRespUser, ldapId string) (*[]Ld
if !found {
newUser := &User{
Owner: owner,
Name: respUser.buildLdapUserName(),
Name: syncUser.buildLdapUserName(),
CreatedTime: util.GetCurrentTime(),
DisplayName: respUser.buildLdapDisplayName(),
DisplayName: syncUser.buildLdapDisplayName(),
Avatar: organization.DefaultAvatar,
Email: respUser.Email,
Phone: respUser.Phone,
Address: []string{respUser.Address},
Email: syncUser.Email,
Phone: syncUser.Phone,
Address: []string{syncUser.Address},
Affiliation: affiliation,
Tag: tag,
Score: beego.AppConfig.DefaultInt("initScore", 2000),
Ldap: respUser.Uuid,
Ldap: syncUser.Uuid,
}
affected := AddUser(newUser)
if !affected {
failedUsers = append(failedUsers, respUser)
failedUsers = append(failedUsers, syncUser)
continue
}
}
}
return &existUsers, &failedUsers
return existUsers, failedUsers, err
}
func GetExistUuids(owner string, uuids []string) []string {
var users []User
var existUuids []string
existUuidSet := make(map[string]struct{})
err := adapter.Engine.Where(fmt.Sprintf("ldap IN (%s) AND owner = ?", "'"+strings.Join(uuids, "','")+"'"), owner).Find(&users)
err := adapter.Engine.Table("user").Where("owner = ?", owner).Cols("ldap").
In("ldap", uuids).Select("DISTINCT ldap").Find(&existUuids)
if err != nil {
panic(err)
}
if len(users) > 0 {
for _, result := range users {
existUuidSet[result.Ldap] = struct{}{}
}
}
for uuid := range existUuidSet {
existUuids = append(existUuids, uuid)
}
return existUuids
}

View File

@@ -235,6 +235,16 @@ func GetPermissionsByRole(roleId string) []*Permission {
return permissions
}
func GetPermissionsByResource(resourceId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("resources like ?", "%"+resourceId+"\"%").Find(&permissions)
if err != nil {
panic(err)
}
return permissions
}
func GetPermissionsBySubmitter(owner string, submitter string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Desc("created_time").Find(&permissions, &Permission{Owner: owner, Submitter: submitter})

View File

@@ -47,7 +47,8 @@ type Record struct {
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
Action string `xorm:"varchar(1000)" json:"action"`
ExtendedUser *User `xorm:"-" json:"extendedUser"`
Object string `xorm:"-" json:"object"`
ExtendedUser *User `xorm:"-" json:"extendedUser"`
IsTriggered bool `json:"isTriggered"`
}
@@ -60,6 +61,11 @@ func NewRecord(ctx *context.Context) *Record {
requestUri = requestUri[0:1000]
}
object := ""
if ctx.Input.RequestBody != nil && len(ctx.Input.RequestBody) != 0 {
object = string(ctx.Input.RequestBody)
}
record := Record{
Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(),
@@ -68,6 +74,7 @@ func NewRecord(ctx *context.Context) *Record {
Method: ctx.Request.Method,
RequestUri: requestUri,
Action: action,
Object: object,
IsTriggered: false,
}
return &record
@@ -159,7 +166,7 @@ func SendWebhooks(record *Record) error {
if matched {
if webhook.IsUserExtended {
user := getUser(record.Organization, record.User)
user := GetMaskedUser(getUser(record.Organization, record.User))
record.ExtendedUser = user
}

View File

@@ -224,6 +224,9 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
nowTime := time.Now()
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
if application.RefreshExpireInHours == 0 {
refreshExpireTime = expireTime
}
user = refineUser(user)

View File

@@ -472,6 +472,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) bool {
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
"signin_wrong_times", "last_signin_wrong_time",
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom",
}
}
if isAdmin {

View File

@@ -550,7 +550,7 @@ class App extends Component {
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/chats" render={(props) => this.renderLoginIfNotLoggedIn(<ChatListPage account={this.state.account} {...props} />)} />
<Route exact path="/chats/:chatName" render={(props) => this.renderLoginIfNotLoggedIn(<ChatEditPage account={this.state.account} {...props} />)} />
<Route exact path="/chat" render={(props) => this.renderLoginIfNotLoggedIn(<ChatPage account={this.state.account} {...props} />)} />

View File

@@ -112,7 +112,6 @@ class ApplicationEditPage extends React.Component {
UNSAFE_componentWillMount() {
this.getApplication();
this.getOrganizations();
this.getCerts();
this.getProviders();
this.getSamlMetadata();
}
@@ -126,6 +125,8 @@ class ApplicationEditPage extends React.Component {
this.setState({
application: application,
});
this.getCerts(application.organization);
});
}
@@ -144,8 +145,8 @@ class ApplicationEditPage extends React.Component {
});
}
getCerts() {
CertBackend.getCerts(this.props.account.owner)
getCerts(owner) {
CertBackend.getCerts(owner)
.then((res) => {
this.setState({
certs: (res.msg === undefined) ? res : [],

View File

@@ -65,6 +65,7 @@ class ApplicationListPage extends BaseListPage {
redirectUris: ["http://localhost:9000/callback"],
tokenFormat: "JWT",
expireInHours: 24 * 7,
refreshExpireInHours: 24 * 7,
formOffset: 2,
};
}

View File

@@ -15,6 +15,7 @@
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import * as CertBackend from "./backend/CertBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
import copy from "copy-to-clipboard";
@@ -29,6 +30,7 @@ class CertEditPage extends React.Component {
this.state = {
classes: props,
certName: props.match.params.certName,
owner: props.match.params.organizationName,
cert: null,
organizations: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
@@ -37,10 +39,11 @@ class CertEditPage extends React.Component {
UNSAFE_componentWillMount() {
this.getCert();
this.getOrganizations();
}
getCert() {
CertBackend.getCert(this.props.account.owner, this.state.certName)
CertBackend.getCert(this.state.owner, this.state.certName)
.then((cert) => {
this.setState({
cert: cert,
@@ -48,6 +51,15 @@ class CertEditPage extends React.Component {
});
}
getOrganizations() {
OrganizationBackend.getOrganizations("admin")
.then((res) => {
this.setState({
organizations: (res.msg === undefined) ? res : [],
});
});
}
parseCertField(key, value) {
if (["port"].includes(key)) {
value = Setting.myParseInt(value);
@@ -230,7 +242,7 @@ class CertEditPage extends React.Component {
submitCertEdit(willExist) {
const cert = Setting.deepCopy(this.state.cert);
CertBackend.updateCert(this.state.cert.owner, this.state.certName, cert)
CertBackend.updateCert(this.state.owner, this.state.certName, cert)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved"));
@@ -241,7 +253,7 @@ class CertEditPage extends React.Component {
if (willExist) {
this.props.history.push("/certs");
} else {
this.props.history.push(`/certs/${this.state.cert.name}`);
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);

View File

@@ -23,10 +23,20 @@ import BaseListPage from "./BaseListPage";
import PopconfirmModal from "./common/modal/PopconfirmModal";
class CertListPage extends BaseListPage {
constructor(props) {
super(props);
}
componentDidMount() {
this.setState({
owner: Setting.isAdminUser(this.props.account) ? "admin" : this.props.account.owner,
});
}
newCert() {
const randomName = Setting.getRandomName();
return {
owner: this.props.account.owner, // this.props.account.certname,
owner: this.state.owner,
name: `cert_${randomName}`,
createdTime: moment().format(),
displayName: `New Cert - ${randomName}`,
@@ -45,7 +55,7 @@ class CertListPage extends BaseListPage {
CertBackend.addCert(newCert)
.then((res) => {
if (res.status === "ok") {
this.props.history.push({pathname: `/certs/${newCert.name}`, mode: "add"});
this.props.history.push({pathname: `/certs/${newCert.owner}/${newCert.name}`, mode: "add"});
Setting.showMessage("success", i18next.t("general:Successfully added"));
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
@@ -86,7 +96,7 @@ class CertListPage extends BaseListPage {
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/certs/${text}`}>
<Link to={`/certs/${record.owner}/${text}`}>
{text}
</Link>
);
@@ -99,6 +109,9 @@ class CertListPage extends BaseListPage {
width: "150px",
sorter: true,
...this.getColumnSearchProps("organization"),
render: (text, record, index) => {
return (text !== "admin") ? text : i18next.t("provider:admin (Shared)");
},
},
{
title: i18next.t("general:Created time"),
@@ -176,7 +189,7 @@ class CertListPage extends BaseListPage {
render: (text, record, index) => {
return (
<div>
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)}
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}

View File

@@ -94,7 +94,7 @@ class LdapSyncPage extends React.Component {
if (res.status === "ok") {
this.setState((prevState) => {
prevState.users = res.data.users;
prevState.existUuids = res.data2?.length > 0 ? res.data2 : [];
prevState.existUuids = res.data2?.length > 0 ? res.data2.filter(uuid => uuid !== "") : [];
return prevState;
});
} else {
@@ -210,7 +210,7 @@ class LdapSyncPage extends React.Component {
});
},
getCheckboxProps: record => ({
disabled: this.state.existUuids.indexOf(record.uuid) !== -1,
disabled: this.state.existUuids.indexOf(record.uuid) !== -1 || record.uidNumber === "",
}),
};

View File

@@ -200,6 +200,22 @@ class OrganizationEditPage extends React.Component {
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Languages"), i18next.t("general:Languages - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}}
options={Setting.Countries.map((item) => {
return Setting.getOption(item.label, item.key);
})}
value={this.state.organization.languages ?? []}
onChange={(value => {
this.updateOrganizationField("languages", value);
})} >
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Default avatar"), i18next.t("general:Default avatar - Tooltip"))} :
@@ -259,22 +275,6 @@ class OrganizationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Languages"), i18next.t("general:Languages - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}}
options={Setting.Countries.map((item) => {
return Setting.getOption(item.label, item.key);
})}
value={this.state.organization.languages ?? []}
onChange={(value => {
this.updateOrganizationField("languages", value);
})} >
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :

View File

@@ -112,6 +112,9 @@ class ProviderListPage extends BaseListPage {
width: "150px",
sorter: true,
...this.getColumnSearchProps("organization"),
render: (text, record, index) => {
return (text !== "admin") ? text : i18next.t("provider:admin (Shared)");
},
},
{
title: i18next.t("general:Created time"),

View File

@@ -28,6 +28,20 @@ require("codemirror/mode/javascript/javascript");
const {Option} = Select;
const applicationTemplate = {
owner: "admin", // this.props.account.applicationName,
name: "application_123",
organization: "built-in",
createdTime: "2022-01-01T01:03:42+08:00",
displayName: "New Application - 123",
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
enablePassword: true,
enableSignUp: true,
enableSigninSession: false,
enableCodeSignin: false,
enableSamlCompress: false,
};
const previewTemplate = {
"id": 9078,
"owner": "built-in",
@@ -37,9 +51,10 @@ const previewTemplate = {
"clientIp": "159.89.126.192",
"user": "admin",
"method": "POST",
"requestUri": "/api/login",
"requestUri": "/api/add-application",
"action": "login",
"isTriggered": false,
"object": JSON.stringify(applicationTemplate),
};
const userTemplate = {
@@ -49,7 +64,7 @@ const userTemplate = {
"updatedTime": "",
"id": "9eb20f79-3bb5-4e74-99ac-39e3b9a171e8",
"type": "normal-user",
"password": "123",
"password": "***",
"passwordSalt": "",
"displayName": "Admin",
"avatar": "https://cdn.casbin.com/usercontent/admin/avatar/1596241359.png",

View File

@@ -242,6 +242,7 @@ class LoginPage extends React.Component {
if (resp.msg === RequiredMfa) {
Setting.goToLink(`/prompt/${application.name}?redirectUri=${oAuthParams.redirectUri}&code=${code}&state=${oAuthParams.state}&promptType=mfa`);
return;
}
if (Setting.isPromptAnswered(account, application)) {