Add Link API.

This commit is contained in:
Yang Luo 2021-04-19 01:14:41 +08:00
parent 6774b0379c
commit 36895801f0
14 changed files with 143 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 (
<img width={30} height={30} src={url} alt={idp} />

View File

@ -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 (
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={2}>
{
Setting.getIdpLogo(idp.toLowerCase())
Setting.getProviderLogo(provider)
}
<span style={{marginLeft: '5px'}}>
{
`${idp}:`
`${provider.type}:`
}
</span>
</Col>
<Col span={22} >
<span style={{width: '200px', display: "inline-block"}}>
{
this.state.user[idp.toLowerCase()] === "" ? (
linkedValue === "" ? (
"(empty)"
) : (
<a target="_blank" rel="noreferrer" href={this.getIdpLink(idp.toLowerCase(), this.state.user[idp.toLowerCase()])}>
<a target="_blank" rel="noreferrer" href={this.getProviderLink(provider, linkedValue)}>
{
this.state.user[idp.toLowerCase()]
linkedValue
}
</a>
)
}
</span>
{
this.state.user[idp.toLowerCase()] === "" ? (
<Button style={{marginLeft: '20px', width: '80px'}} type="primary" onClick={() => this.linkUser(idp)}>Link</Button>
linkedValue === "" ? (
<a key={provider.displayName} href={Provider.getAuthUrl(this.state.application, provider, "link")}>
<Button style={{marginLeft: '20px', width: '80px'}} type="primary">Link</Button>
</a>
) : (
<Button style={{marginLeft: '20px', width: '80px'}} onClick={() => this.unlinkUser(idp)}>Unlink</Button>
<Button style={{marginLeft: '20px', width: '80px'}} onClick={() => this.unlinkUser(provider.type)}>Unlink</Button>
)
}
</Col>
@ -286,16 +312,7 @@ class UserEditPage extends React.Component {
</Col>
</Row>
{
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 : (

View File

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

View File

@ -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({

View File

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

View File

@ -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") {

View File

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

View File

@ -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)}`, {

View File

@ -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",