feat: add defaultApplication for Orgnization (#1111)

* feat: add defaultApplication for Orgnization

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: remove redundant codes

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: don't use app-built-in

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: add query param

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* Update organization.go

* Update organization.go

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
Co-authored-by: Yang Luo <hsluoyz@qq.com>
This commit is contained in:
Yixiang Zhao
2022-09-10 20:41:45 +08:00
committed by GitHub
parent c661a57cb2
commit b6cdc46023
16 changed files with 125 additions and 13 deletions

View File

@ -110,6 +110,7 @@ p, *, *, GET, /api/saml/metadata, *, *
p, *, *, *, /cas, *, * p, *, *, *, /cas, *, *
p, *, *, *, /api/webauthn, *, * p, *, *, *, /api/webauthn, *, *
p, *, *, GET, /api/get-release, *, * p, *, *, GET, /api/get-release, *, *
p, *, *, GET, /api/get-default-application, *, *
` `
sa := stringadapter.NewAdapter(ruleText) sa := stringadapter.NewAdapter(ruleText)

View File

@ -121,3 +121,23 @@ func (c *ApiController) DeleteOrganization() {
c.Data["json"] = wrapActionResponse(object.DeleteOrganization(&organization)) c.Data["json"] = wrapActionResponse(object.DeleteOrganization(&organization))
c.ServeJSON() c.ServeJSON()
} }
// GetDefaultApplication ...
// @Title GetDefaultApplication
// @Tag Organization API
// @Description get default application
// @Param id query string true "organization id"
// @Success 200 {object} Response The Response object
// @router /get-default-application [get]
func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
application := object.GetMaskedApplication(object.GetDefaultApplication(id), userId)
if application == nil {
c.ResponseError("Please set a default application for this organization")
return
}
c.ResponseOk(application)
}

View File

@ -41,6 +41,7 @@ type Organization struct {
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"` PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"` PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"` DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"` MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
EnableSoftDeletion bool `json:"enableSoftDeletion"` EnableSoftDeletion bool `json:"enableSoftDeletion"`
@ -216,3 +217,32 @@ func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, str
} }
return true, "" return true, ""
} }
func GetDefaultApplication(id string) *Application {
organization := GetOrganization(id)
if organization == nil {
return nil
}
if organization.DefaultApplication != "" {
return getApplication("admin", organization.DefaultApplication)
}
applications := []*Application{}
err := adapter.Engine.Asc("created_time").Find(&applications, &Application{Organization: organization.Name})
if err != nil {
panic(err)
}
if len(applications) == 0 {
return nil
}
for _, application := range applications {
if application.EnableSignUp {
return application
}
}
return applications[0]
}

View File

@ -60,6 +60,7 @@ func initAPI() {
beego.Router("/api/update-organization", &controllers.ApiController{}, "POST:UpdateOrganization") beego.Router("/api/update-organization", &controllers.ApiController{}, "POST:UpdateOrganization")
beego.Router("/api/add-organization", &controllers.ApiController{}, "POST:AddOrganization") beego.Router("/api/add-organization", &controllers.ApiController{}, "POST:AddOrganization")
beego.Router("/api/delete-organization", &controllers.ApiController{}, "POST:DeleteOrganization") beego.Router("/api/delete-organization", &controllers.ApiController{}, "POST:DeleteOrganization")
beego.Router("/api/get-default-application", &controllers.ApiController{}, "GET:GetDefaultApplication")
beego.Router("/api/get-global-users", &controllers.ApiController{}, "GET:GetGlobalUsers") beego.Router("/api/get-global-users", &controllers.ApiController{}, "GET:GetGlobalUsers")
beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers") beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers")

View File

@ -388,16 +388,15 @@ class App extends Component {
</Link> </Link>
</Menu.Item> </Menu.Item>
); );
res.push(
<Menu.Item key="/permissions">
<Link to="/permissions">
{i18next.t("general:Permissions")}
</Link>
</Menu.Item>
);
} }
res.push(
<Menu.Item key="/permissions">
<Link to="/permissions">
{i18next.t("general:Permissions")}
</Link>
</Menu.Item>
);
if (Setting.isAdminUser(this.state.account)) { if (Setting.isAdminUser(this.state.account)) {
res.push( res.push(
<Menu.Item key="/models"> <Menu.Item key="/models">

View File

@ -15,6 +15,7 @@
import React from "react"; import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from "antd"; import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
@ -31,6 +32,7 @@ class OrganizationEditPage extends React.Component {
classes: props, classes: props,
organizationName: props.match.params.organizationName, organizationName: props.match.params.organizationName,
organization: null, organization: null,
applications: [],
ldaps: null, ldaps: null,
mode: props.location.mode !== undefined ? props.location.mode : "edit", mode: props.location.mode !== undefined ? props.location.mode : "edit",
}; };
@ -38,6 +40,7 @@ class OrganizationEditPage extends React.Component {
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getOrganization(); this.getOrganization();
this.getApplications();
this.getLdaps(); this.getLdaps();
} }
@ -50,6 +53,15 @@ class OrganizationEditPage extends React.Component {
}); });
} }
getApplications() {
ApplicationBackend.getApplicationsByOrganization("admin", this.state.organizationName)
.then((applications) => {
this.setState({
applications: applications,
});
});
}
getLdaps() { getLdaps() {
LdapBackend.getLdaps(this.state.organizationName) LdapBackend.getLdaps(this.state.organizationName)
.then(res => { .then(res => {
@ -209,6 +221,18 @@ class OrganizationEditPage extends React.Component {
</Row> </Row>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Default application"), i18next.t("general:Default application - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}>
{
this.state.applications?.map((item, index) => <Option key={index} value={item.name}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Tags"), i18next.t("organization:Tags - Tooltip"))} : {Setting.getLabel(i18next.t("organization:Tags"), i18next.t("organization:Tags - Tooltip"))} :

View File

@ -35,6 +35,7 @@ class OrganizationListPage extends BaseListPage {
PasswordSalt: "", PasswordSalt: "",
phonePrefix: "86", phonePrefix: "86",
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`, defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
defaultApplication: "",
tags: [], tags: [],
masterPassword: "", masterPassword: "",
enableSoftDeletion: false, enableSoftDeletion: false,

View File

@ -18,6 +18,7 @@ import {Button, Checkbox, Col, Form, Input, Result, Row, Spin, Tabs} from "antd"
import {LockOutlined, UserOutlined} from "@ant-design/icons"; import {LockOutlined, UserOutlined} from "@ant-design/icons";
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend"; import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as OrganizationBackend from "../backend/OrganizationBackend";
import * as ApplicationBackend from "../backend/ApplicationBackend"; import * as ApplicationBackend from "../backend/ApplicationBackend";
import * as Provider from "./Provider"; import * as Provider from "./Provider";
import * as ProviderButton from "./ProviderButton"; import * as ProviderButton from "./ProviderButton";
@ -90,12 +91,26 @@ class LoginPage extends React.Component {
return; return;
} }
ApplicationBackend.getApplication("admin", this.state.applicationName) if (this.state.owner === null || this.state.owner === undefined || this.state.owner === "") {
.then((application) => { ApplicationBackend.getApplication("admin", this.state.applicationName)
this.setState({ .then((application) => {
application: application, this.setState({
application: application,
});
}); });
}); } else {
OrganizationBackend.getDefaultApplication("admin", this.state.owner)
.then((res) => {
if (res.status === "ok") {
this.setState({
application: res.data,
applicationName: res.data.name,
});
} else {
Util.showMessage("error", res.msg);
}
});
}
} }
getSamlApplication() { getSamlApplication() {

View File

@ -54,3 +54,10 @@ export function deleteOrganization(organization) {
body: JSON.stringify(newOrganization), body: JSON.stringify(newOrganization),
}).then(res => res.json()); }).then(res => res.json());
} }
export function getDefaultApplication(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-default-application?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}

View File

@ -124,6 +124,8 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "Client-IP", "Client IP": "Client-IP",
"Created time": "Erstellte Zeit", "Created time": "Erstellte Zeit",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Standard Avatar", "Default avatar": "Standard Avatar",
"Default avatar - Tooltip": "default avatar", "Default avatar - Tooltip": "default avatar",
"Delete": "Löschen", "Delete": "Löschen",

View File

@ -124,6 +124,8 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "Client IP", "Client IP": "Client IP",
"Created time": "Created time", "Created time": "Created time",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar", "Default avatar": "Default avatar",
"Default avatar - Tooltip": "Default avatar - Tooltip", "Default avatar - Tooltip": "Default avatar - Tooltip",
"Delete": "Delete", "Delete": "Delete",

View File

@ -124,6 +124,8 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "IP du client", "Client IP": "IP du client",
"Created time": "Date de création", "Created time": "Date de création",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Avatar par défaut", "Default avatar": "Avatar par défaut",
"Default avatar - Tooltip": "default avatar", "Default avatar - Tooltip": "default avatar",
"Delete": "Supprimez", "Delete": "Supprimez",

View File

@ -124,6 +124,8 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "クライアント IP", "Client IP": "クライアント IP",
"Created time": "作成日時", "Created time": "作成日時",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "デフォルトのアバター", "Default avatar": "デフォルトのアバター",
"Default avatar - Tooltip": "default avatar", "Default avatar - Tooltip": "default avatar",
"Delete": "削除", "Delete": "削除",

View File

@ -124,6 +124,8 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "Client IP", "Client IP": "Client IP",
"Created time": "Created time", "Created time": "Created time",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar", "Default avatar": "Default avatar",
"Default avatar - Tooltip": "default avatar", "Default avatar - Tooltip": "default avatar",
"Delete": "Delete", "Delete": "Delete",

View File

@ -124,6 +124,8 @@
"Click to Upload": "Нажмите здесь, чтобы загрузить", "Click to Upload": "Нажмите здесь, чтобы загрузить",
"Client IP": "IP клиента", "Client IP": "IP клиента",
"Created time": "Время создания", "Created time": "Время создания",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Аватар по умолчанию", "Default avatar": "Аватар по умолчанию",
"Default avatar - Tooltip": "default avatar", "Default avatar - Tooltip": "default avatar",
"Delete": "Удалить", "Delete": "Удалить",

View File

@ -124,6 +124,8 @@
"Click to Upload": "点击上传", "Click to Upload": "点击上传",
"Client IP": "客户端IP", "Client IP": "客户端IP",
"Created time": "创建时间", "Created time": "创建时间",
"Default application": "默认应用",
"Default application - Tooltip": "默认应用",
"Default avatar": "默认头像", "Default avatar": "默认头像",
"Default avatar - Tooltip": "默认头像", "Default avatar - Tooltip": "默认头像",
"Delete": "删除", "Delete": "删除",