diff --git a/controllers/application.go b/controllers/application.go index eb27f8fd..a6976bac 100644 --- a/controllers/application.go +++ b/controllers/application.go @@ -44,6 +44,18 @@ func (c *ApiController) GetApplication() { c.ServeJSON() } +// @Title GetDefaultApplication +// @Description get the detail of the default application +// @Param owner query string true "The owner of the application." +// @Success 200 {object} object.Application The Response object +// @router /get-default-application [get] +func (c *ApiController) GetDefaultApplication() { + owner := c.Input().Get("owner") + + c.Data["json"] = object.GetDefaultApplication(owner) + c.ServeJSON() +} + // @Title UpdateApplication // @Description update an application // @Param id query string true "The id of the application" diff --git a/controllers/provider.go b/controllers/provider.go index 2acb4e4c..ff820587 100644 --- a/controllers/provider.go +++ b/controllers/provider.go @@ -32,6 +32,18 @@ func (c *ApiController) GetProviders() { c.ServeJSON() } +// @Title GetDefaultProviders +// @Description get default providers +// @Param owner query string true "The owner of providers" +// @Success 200 {array} object.Provider The Response object +// @router /get-default-providers [get] +func (c *ApiController) GetDefaultProviders() { + owner := c.Input().Get("owner") + + c.Data["json"] = object.GetDefaultProviders(owner) + c.ServeJSON() +} + // @Title GetProvider // @Description get provider // @Param id query string true "The id of the provider" diff --git a/object/application.go b/object/application.go index d0fa8c62..34f172d1 100644 --- a/object/application.go +++ b/object/application.go @@ -80,6 +80,22 @@ func getApplication(owner string, name string) *Application { } } +func GetDefaultApplication(owner string) *Application { + name := "app-built-in" + application := Application{Owner: owner, Name: name} + existed, err := adapter.engine.Get(&application) + if err != nil { + panic(err) + } + + if existed { + extendApplication(&application) + return &application + } else { + return nil + } +} + func getApplicationByClientId(clientId string) *Application { application := Application{} existed, err := adapter.engine.Where("client_id=?", clientId).Get(&application) diff --git a/object/provider.go b/object/provider.go index 1897bc47..9630c696 100644 --- a/object/provider.go +++ b/object/provider.go @@ -15,6 +15,8 @@ package object import ( + "strings" + "github.com/casdoor/casdoor/util" "xorm.io/core" ) @@ -41,6 +43,17 @@ func GetProviders(owner string) []*Provider { return providers } +func GetDefaultProviders(owner string) []*Provider { + providers := GetProviders(owner) + res := []*Provider{} + for _, provider := range providers { + if strings.Contains(provider.Name, "casdoor") { + res = append(res, provider) + } + } + return res +} + func getProvider(owner string, name string) *Provider { provider := Provider{Owner: owner, Name: name} existed, err := adapter.engine.Get(&provider) diff --git a/routers/router.go b/routers/router.go index baa94c9e..fc2229a5 100644 --- a/routers/router.go +++ b/routers/router.go @@ -61,6 +61,7 @@ func initAPI() { beego.Router("/api/upload-avatar", &controllers.ApiController{}, "POST:UploadAvatar") beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders") + beego.Router("/api/get-default-providers", &controllers.ApiController{}, "GET:GetDefaultProviders") beego.Router("/api/get-provider", &controllers.ApiController{}, "GET:GetProvider") beego.Router("/api/update-provider", &controllers.ApiController{}, "POST:UpdateProvider") beego.Router("/api/add-provider", &controllers.ApiController{}, "POST:AddProvider") @@ -68,6 +69,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-default-application", &controllers.ApiController{}, "GET:GetDefaultApplication") 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") diff --git a/web/src/Setting.js b/web/src/Setting.js index be94a2e8..49fe10d4 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -172,7 +172,8 @@ export function getClickable(text) { ) } -export function getIdpLogo(idp) { +export function getProviderLogo(provider) { + const idp = provider.type.toLowerCase(); const url = `https://cdn.jsdelivr.net/gh/casbin/static/img/social_${idp}.png`; return ( {idp} diff --git a/web/src/UserEditPage.js b/web/src/UserEditPage.js index a2c29083..ab5be93c 100644 --- a/web/src/UserEditPage.js +++ b/web/src/UserEditPage.js @@ -21,6 +21,9 @@ import {LinkOutlined} from "@ant-design/icons"; import i18next from "i18next"; import CropperDiv from "./CropperDiv.js"; import * as AuthBackend from "./auth/AuthBackend"; +import * as ApplicationBackend from "./backend/ApplicationBackend"; +import * as ProviderBackend from "./backend/ProviderBackend"; +import * as Provider from "./auth/Provider"; const { Option } = Select; @@ -32,13 +35,17 @@ class UserEditPage extends React.Component { organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, userName: props.userName !== undefined ? props.userName : props.match.params.userName, user: null, + application: null, organizations: [], + providers: [], }; } UNSAFE_componentWillMount() { this.getUser(); this.getOrganizations(); + this.getDefaultApplication(); + this.getDefaultProviders(); } getUser() { @@ -59,6 +66,24 @@ class UserEditPage extends React.Component { }); } + getDefaultApplication() { + ApplicationBackend.getDefaultApplication("admin") + .then((application) => { + this.setState({ + application: application, + }); + }); + } + + getDefaultProviders() { + ProviderBackend.getDefaultProviders("admin") + .then((res) => { + this.setState({ + providers: res, + }); + }); + } + parseUserField(key, value) { // if ([].includes(key)) { // value = Setting.myParseInt(value); @@ -76,9 +101,6 @@ class UserEditPage extends React.Component { }); } - linkUser(providerType) { - } - unlinkUser(providerType) { const body = { providerType: providerType, @@ -95,48 +117,52 @@ class UserEditPage extends React.Component { }); } - getIdpLink(idp, username) { - if (idp === "github") { - return `https://github.com/${username}`; - } else if (idp === "google") { + getProviderLink(provider, linkedValue) { + if (provider.type === "GitHub") { + return `https://github.com/${linkedValue}`; + } else if (provider.type === "Google") { return "https://mail.google.com"; } else { return ""; } } - renderIdp(idp) { + renderIdp(provider) { + const linkedValue = this.state.user[provider.type.toLowerCase()]; + return ( { - Setting.getIdpLogo(idp.toLowerCase()) + Setting.getProviderLogo(provider) } { - `${idp}:` + `${provider.type}:` } { - this.state.user[idp.toLowerCase()] === "" ? ( + linkedValue === "" ? ( "(empty)" ) : ( - + { - this.state.user[idp.toLowerCase()] + linkedValue } ) } { - this.state.user[idp.toLowerCase()] === "" ? ( - + linkedValue === "" ? ( + + + ) : ( - + ) } @@ -286,16 +312,7 @@ class UserEditPage extends React.Component { { - this.renderIdp("GitHub") - } - { - this.renderIdp("Google") - } - { - this.renderIdp("QQ") - } - { - this.renderIdp("WeChat") + this.state.providers.map((provider, index) => this.renderIdp(provider)) } { !Setting.isAdminUser(this.props.account) ? null : ( diff --git a/web/src/auth/AuthBackend.js b/web/src/auth/AuthBackend.js index 1987b563..4d783ba7 100644 --- a/web/src/auth/AuthBackend.js +++ b/web/src/auth/AuthBackend.js @@ -63,10 +63,3 @@ export function unlink(values) { body: JSON.stringify(values), }).then(res => res.json()); } - -export function getApplication(owner, name) { - return fetch(`${authConfig.serverUrl}/api/get-application?id=${owner}/${encodeURIComponent(name)}`, { - method: "GET", - credentials: "include" - }).then(res => res.json()); -} diff --git a/web/src/auth/AuthCallback.js b/web/src/auth/AuthCallback.js index 6428716a..a5f6c525 100644 --- a/web/src/auth/AuthCallback.js +++ b/web/src/auth/AuthCallback.js @@ -34,7 +34,8 @@ class AuthCallback extends React.Component { // realRedirectUrl = "http://localhost:9000" const params = new URLSearchParams(this.props.location.search); const state = params.get("state"); - return new URLSearchParams(Util.stateToGetQueryParams(state)); + const queryString = Util.stateToGetQueryParams(state); + return new URLSearchParams(queryString); } getResponseType() { @@ -42,14 +43,21 @@ class AuthCallback extends React.Component { const authServerUrl = authConfig.serverUrl; const innerParams = this.getInnerParams(); - const realRedirectUri = innerParams.get("redirect_uri"); - const realRedirectUrl = new URL(realRedirectUri).origin; + const method = innerParams.get("method"); + if (method === "signup") { + const realRedirectUri = innerParams.get("redirect_uri"); + const realRedirectUrl = new URL(realRedirectUri).origin; - // For Casdoor itself, we use "login" directly - if (authServerUrl === realRedirectUrl) { - return "login"; + // For Casdoor itself, we use "login" directly + if (authServerUrl === realRedirectUrl) { + return "login"; + } else { + return "code"; + } + } else if (method === "link") { + return "link"; } else { - return "code"; + return "unknown"; } } @@ -65,7 +73,8 @@ class AuthCallback extends React.Component { application: applicationName, provider: providerName, code: params.get("code"), - state: innerParams.get("state"), + // state: innerParams.get("state"), + state: innerParams.get("application"), redirectUri: redirectUri, method: method, }; @@ -81,6 +90,9 @@ class AuthCallback extends React.Component { const code = res.data; Setting.goToLink(`${oAuthParams.redirectUri}?code=${code}&state=${oAuthParams.state}`); // Util.showMessage("success", `Authorization code: ${res.data}`); + } else if (responseType === "link") { + const from = innerParams.get("from"); + Setting.goToLinkSoft(this, from); } } else { this.setState({ diff --git a/web/src/auth/LoginPage.js b/web/src/auth/LoginPage.js index 09b97fa6..38ef5f79 100644 --- a/web/src/auth/LoginPage.js +++ b/web/src/auth/LoginPage.js @@ -17,6 +17,7 @@ import {Link} from "react-router-dom"; import {Button, Checkbox, Col, Form, Input, Row} from "antd"; import {LockOutlined, UserOutlined} from "@ant-design/icons"; import * as AuthBackend from "./AuthBackend"; +import * as ApplicationBackend from "../backend/ApplicationBackend"; import * as Provider from "./Provider"; import * as Util from "./Util"; import * as Setting from "../Setting"; @@ -68,7 +69,7 @@ class LoginPage extends React.Component { return; } - AuthBackend.getApplication("admin", this.state.applicationName) + ApplicationBackend.getApplication("admin", this.state.applicationName) .then((application) => { this.setState({ application: application, diff --git a/web/src/auth/Provider.js b/web/src/auth/Provider.js index 86c45165..67b34abd 100644 --- a/web/src/auth/Provider.js +++ b/web/src/auth/Provider.js @@ -43,6 +43,10 @@ export function getAuthLogo(provider) { } export function getAuthUrl(application, provider, method) { + if (application === null || provider === null) { + return ""; + } + const redirectUri = `${window.location.origin}/callback`; const state = Util.getQueryParamsToState(application.name, provider.name, method); if (provider.type === "Google") { diff --git a/web/src/auth/Util.js b/web/src/auth/Util.js index ab1dbf34..a4f8f724 100644 --- a/web/src/auth/Util.js +++ b/web/src/auth/Util.js @@ -111,6 +111,9 @@ export function getOAuthGetParameters(params) { export function getQueryParamsToState(applicationName, providerName, method) { let query = window.location.search; query = `${query}&application=${applicationName}&provider=${providerName}&method=${method}`; + if (method === "link") { + query = `${query}&from=${window.location.pathname}`; + } return btoa(query); } diff --git a/web/src/backend/ApplicationBackend.js b/web/src/backend/ApplicationBackend.js index 68f5dc54..a85500f5 100644 --- a/web/src/backend/ApplicationBackend.js +++ b/web/src/backend/ApplicationBackend.js @@ -28,6 +28,13 @@ export function getApplication(owner, name) { }).then(res => res.json()); } +export function getDefaultApplication(owner) { + return fetch(`${Setting.ServerUrl}/api/get-default-application?owner=${owner}`, { + method: "GET", + credentials: "include" + }).then(res => res.json()); +} + export function updateApplication(owner, name, application) { let newApplication = Setting.deepCopy(application); return fetch(`${Setting.ServerUrl}/api/update-application?id=${owner}/${encodeURIComponent(name)}`, { diff --git a/web/src/backend/ProviderBackend.js b/web/src/backend/ProviderBackend.js index 9a055163..61177ea5 100644 --- a/web/src/backend/ProviderBackend.js +++ b/web/src/backend/ProviderBackend.js @@ -21,6 +21,13 @@ export function getProviders(owner) { }).then(res => res.json()); } +export function getDefaultProviders(owner) { + return fetch(`${Setting.ServerUrl}/api/get-default-providers?owner=${owner}`, { + method: "GET", + credentials: "include" + }).then(res => res.json()); +} + export function getProvider(owner, name) { return fetch(`${Setting.ServerUrl}/api/get-provider?id=${owner}/${encodeURIComponent(name)}`, { method: "GET",