mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 10:45:47 +08:00
feat: add gitlab provider (#273)
Signed-off-by: sh1luo <690898835@qq.com>
This commit is contained in:
parent
e1182bb635
commit
75e917a070
230
idp/gitlab.go
Normal file
230
idp/gitlab.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
// Copyright 2021 The casbin 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 idp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitlabIdProvider struct {
|
||||||
|
Client *http.Client
|
||||||
|
Config *oauth2.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitlabIdProvider(clientId string, clientSecret string, redirectUrl string) *GitlabIdProvider {
|
||||||
|
idp := &GitlabIdProvider{}
|
||||||
|
|
||||||
|
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||||
|
idp.Config = config
|
||||||
|
|
||||||
|
return idp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (idp *GitlabIdProvider) SetHttpClient(client *http.Client) {
|
||||||
|
idp.Client = 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{
|
||||||
|
TokenURL: "https://gitlab.com/oauth/token",
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = &oauth2.Config{
|
||||||
|
Scopes: []string{"read_user+profile"},
|
||||||
|
Endpoint: endpoint,
|
||||||
|
ClientID: clientId,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
RedirectURL: redirectUrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
type GitlabProviderToken struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
CreatedAt int `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||||
|
// get more detail via: https://docs.gitlab.com/ee/api/oauth2.html
|
||||||
|
func (idp *GitlabIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
|
params := url.Values{}
|
||||||
|
params.Add("grant_type", "authorization_code")
|
||||||
|
params.Add("client_id", idp.Config.ClientID)
|
||||||
|
params.Add("client_secret", idp.Config.ClientSecret)
|
||||||
|
params.Add("code", code)
|
||||||
|
params.Add("redirect_uri", idp.Config.RedirectURL)
|
||||||
|
|
||||||
|
accessTokenUrl := fmt.Sprintf("%s?%s", idp.Config.Endpoint.TokenURL, params.Encode())
|
||||||
|
resp, err := idp.Client.Post(accessTokenUrl, "application/json;charset=UTF-8", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gtoken := &GitlabProviderToken{}
|
||||||
|
if err = json.Unmarshal(data, gtoken); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// gtoken.ExpiresIn always returns 0, so we set Expiry=7200 to avoid verification errors.
|
||||||
|
token := &oauth2.Token{
|
||||||
|
AccessToken: gtoken.AccessToken,
|
||||||
|
TokenType: gtoken.TokenType,
|
||||||
|
RefreshToken: gtoken.RefreshToken,
|
||||||
|
Expiry: time.Unix(time.Now().Unix()+int64(7200), 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"id":5162115,
|
||||||
|
"name":"shiluo",
|
||||||
|
"username":"shiluo",
|
||||||
|
"state":"active",
|
||||||
|
"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/5162115/avatar.png",
|
||||||
|
"web_url":"https://gitlab.com/shiluo",
|
||||||
|
"created_at":"2019-12-23T02:50:10.348Z",
|
||||||
|
"bio":"",
|
||||||
|
"bio_html":"",
|
||||||
|
"location":"China",
|
||||||
|
"public_email":"silo1999@163.com",
|
||||||
|
"skype":"",
|
||||||
|
"linkedin":"",
|
||||||
|
"twitter":"",
|
||||||
|
"website_url":"",
|
||||||
|
"organization":"",
|
||||||
|
"job_title":"",
|
||||||
|
"pronouns":null,
|
||||||
|
"bot":false,
|
||||||
|
"work_information":null,
|
||||||
|
"followers":0,
|
||||||
|
"following":0,
|
||||||
|
"last_sign_in_at":"2019-12-26T13:24:42.941Z",
|
||||||
|
"confirmed_at":"2019-12-23T02:52:10.778Z",
|
||||||
|
"last_activity_on":"2021-08-19",
|
||||||
|
"email":"silo1999@163.com",
|
||||||
|
"theme_id":1,
|
||||||
|
"color_scheme_id":1,
|
||||||
|
"projects_limit":100000,
|
||||||
|
"current_sign_in_at":"2021-08-19T09:46:46.004Z",
|
||||||
|
"identities":[
|
||||||
|
{
|
||||||
|
"provider":"github",
|
||||||
|
"extern_uid":"51157931",
|
||||||
|
"saml_provider_id":null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"can_create_group":true,
|
||||||
|
"can_create_project":true,
|
||||||
|
"two_factor_enabled":false,
|
||||||
|
"external":false,
|
||||||
|
"private_profile":false,
|
||||||
|
"commit_email":"silo1999@163.com",
|
||||||
|
"shared_runners_minutes_limit":null,
|
||||||
|
"extra_shared_runners_minutes_limit":null
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
type GitlabUserInfo struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
State string `json:"state"`
|
||||||
|
AvatarUrl string `json:"avatar_url"`
|
||||||
|
WebUrl string `json:"web_url"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
Bio string `json:"bio"`
|
||||||
|
BioHtml string `json:"bio_html"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
PublicEmail string `json:"public_email"`
|
||||||
|
Skype string `json:"skype"`
|
||||||
|
Linkedin string `json:"linkedin"`
|
||||||
|
Twitter string `json:"twitter"`
|
||||||
|
WebsiteUrl string `json:"website_url"`
|
||||||
|
Organization string `json:"organization"`
|
||||||
|
JobTitle string `json:"job_title"`
|
||||||
|
Pronouns interface{} `json:"pronouns"`
|
||||||
|
Bot bool `json:"bot"`
|
||||||
|
WorkInformation interface{} `json:"work_information"`
|
||||||
|
Followers int `json:"followers"`
|
||||||
|
Following int `json:"following"`
|
||||||
|
LastSignInAt time.Time `json:"last_sign_in_at"`
|
||||||
|
ConfirmedAt time.Time `json:"confirmed_at"`
|
||||||
|
LastActivityOn string `json:"last_activity_on"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
ThemeId int `json:"theme_id"`
|
||||||
|
ColorSchemeId int `json:"color_scheme_id"`
|
||||||
|
ProjectsLimit int `json:"projects_limit"`
|
||||||
|
CurrentSignInAt time.Time `json:"current_sign_in_at"`
|
||||||
|
Identities []struct {
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
ExternUid string `json:"extern_uid"`
|
||||||
|
SamlProviderId interface{} `json:"saml_provider_id"`
|
||||||
|
} `json:"identities"`
|
||||||
|
CanCreateGroup bool `json:"can_create_group"`
|
||||||
|
CanCreateProject bool `json:"can_create_project"`
|
||||||
|
TwoFactorEnabled bool `json:"two_factor_enabled"`
|
||||||
|
External bool `json:"external"`
|
||||||
|
PrivateProfile bool `json:"private_profile"`
|
||||||
|
CommitEmail string `json:"commit_email"`
|
||||||
|
SharedRunnersMinutesLimit interface{} `json:"shared_runners_minutes_limit"`
|
||||||
|
ExtraSharedRunnersMinutesLimit interface{} `json:"extra_shared_runners_minutes_limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserInfo use GitlabProviderToken gotten before return GitlabUserInfo
|
||||||
|
func (idp *GitlabIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
|
resp, err := idp.Client.Get("https://gitlab.com/api/v4/user?access_token="+token.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
guser := GitlabUserInfo{}
|
||||||
|
if err = json.Unmarshal(data, &guser);err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo := UserInfo{
|
||||||
|
Id: strconv.Itoa(guser.Id),
|
||||||
|
Username: guser.Username,
|
||||||
|
DisplayName: guser.Name,
|
||||||
|
AvatarUrl: guser.AvatarUrl,
|
||||||
|
Email: guser.Email,
|
||||||
|
}
|
||||||
|
return &userInfo, nil
|
||||||
|
}
|
@ -57,6 +57,8 @@ func GetIdProvider(providerType string, clientId string, clientSecret string, re
|
|||||||
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
|
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
|
||||||
} else if providerType == "Lark" {
|
} else if providerType == "Lark" {
|
||||||
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
||||||
|
} else if providerType == "GitLab" {
|
||||||
|
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
func SendCodeToPhone(provider *Provider, phone, code string) string {
|
func SendCodeToPhone(provider *Provider, phone, code string) string {
|
||||||
client := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.RegionId, provider.TemplateCode, provider.AppId)
|
client := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.RegionId, provider.TemplateCode, provider.AppId)
|
||||||
if client == nil {
|
if client == nil {
|
||||||
return fmt.Sprintf("Unsupported provide type: %s", provider.Type)
|
return fmt.Sprintf("Unsupported provider type: %s", provider.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
param := make(map[string]string)
|
param := make(map[string]string)
|
||||||
|
@ -58,6 +58,7 @@ type User struct {
|
|||||||
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
||||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||||
|
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||||
|
|
||||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||||
Properties map[string]string `json:"properties"`
|
Properties map[string]string `json:"properties"`
|
||||||
|
@ -77,6 +77,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
{id: 'LinkedIn', name: 'LinkedIn'},
|
{id: 'LinkedIn', name: 'LinkedIn'},
|
||||||
{id: 'WeCom', name: 'WeCom'},
|
{id: 'WeCom', name: 'WeCom'},
|
||||||
{id: 'Lark', name: 'Lark'},
|
{id: 'Lark', name: 'Lark'},
|
||||||
|
{id: 'GitLab', name: 'GitLab'},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else if (provider.category === "Email") {
|
} else if (provider.category === "Email") {
|
||||||
|
32
web/src/auth/GitLabLoginButton.js
Normal file
32
web/src/auth/GitLabLoginButton.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2021 The casbin 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.
|
||||||
|
|
||||||
|
import {createButton} from "react-social-login-buttons";
|
||||||
|
import {StaticBaseUrl} from "../Setting";
|
||||||
|
|
||||||
|
function Icon({ width = 24, height = 24, color }) {
|
||||||
|
return <img src={`${StaticBaseUrl}/buttons/gitlab.svg`} alt="Sign in with GitLab" style={{width: 24, height: 24}} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
text: "Sign in with GitLab",
|
||||||
|
icon: Icon,
|
||||||
|
iconFormat: name => `fa fa-${name}`,
|
||||||
|
style: {background: "rgb(255,255,255)", color: "#000000"},
|
||||||
|
activeStyle: {background: "rgb(100,150,250)"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const GitLabLoginButton = createButton(config);
|
||||||
|
|
||||||
|
export default GitLabLoginButton;
|
@ -32,6 +32,7 @@ import i18next from "i18next";
|
|||||||
import LinkedInLoginButton from "./LinkedInLoginButton";
|
import LinkedInLoginButton from "./LinkedInLoginButton";
|
||||||
import WeComLoginButton from "./WeComLoginButton";
|
import WeComLoginButton from "./WeComLoginButton";
|
||||||
import LarkLoginButton from "./LarkLoginButton";
|
import LarkLoginButton from "./LarkLoginButton";
|
||||||
|
import GitLabLoginButton from "./GitLabLoginButton";
|
||||||
|
|
||||||
class LoginPage extends React.Component {
|
class LoginPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -170,6 +171,8 @@ class LoginPage extends React.Component {
|
|||||||
return <WeComLoginButton text={text} align={"center"} />
|
return <WeComLoginButton text={text} align={"center"} />
|
||||||
} else if (type === "Lark") {
|
} else if (type === "Lark") {
|
||||||
return <LarkLoginButton text={text} align={"center"} />
|
return <LarkLoginButton text={text} align={"center"} />
|
||||||
|
} else if (type === "GitLab") {
|
||||||
|
return <GitLabLoginButton text={text} align={"center"} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
@ -55,10 +55,14 @@ const LinkedInAuthLogo = `${StaticBaseUrl}/img/social_linkedin.png`;
|
|||||||
const WeComAuthUri = "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect";
|
const WeComAuthUri = "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect";
|
||||||
const WeComAuthLogo = `${StaticBaseUrl}/img/social_wecom.png`;
|
const WeComAuthLogo = `${StaticBaseUrl}/img/social_wecom.png`;
|
||||||
|
|
||||||
// const WeComAuthScope = "";
|
// const LarkAuthScope = "";
|
||||||
const LarkAuthUri = "https://open.feishu.cn/open-apis/authen/v1/index";
|
const LarkAuthUri = "https://open.feishu.cn/open-apis/authen/v1/index";
|
||||||
const LarkAuthLogo = `${StaticBaseUrl}/img/social_lark.png`;
|
const LarkAuthLogo = `${StaticBaseUrl}/img/social_lark.png`;
|
||||||
|
|
||||||
|
const GitLabAuthScope = "read_user+profile";
|
||||||
|
const GitLabAuthUri = "https://gitlab.com/oauth/authorize";
|
||||||
|
const GitLabAuthLogo = `${StaticBaseUrl}/img/social_gitlab.png`;
|
||||||
|
|
||||||
export function getAuthLogo(provider) {
|
export function getAuthLogo(provider) {
|
||||||
if (provider.type === "Google") {
|
if (provider.type === "Google") {
|
||||||
return GoogleAuthLogo;
|
return GoogleAuthLogo;
|
||||||
@ -82,6 +86,8 @@ export function getAuthLogo(provider) {
|
|||||||
return WeComAuthLogo;
|
return WeComAuthLogo;
|
||||||
} else if (provider.type === "Lark") {
|
} else if (provider.type === "Lark") {
|
||||||
return LarkAuthLogo;
|
return LarkAuthLogo;
|
||||||
|
} else if (provider.type === "GitLab") {
|
||||||
|
return GitLabAuthLogo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,5 +120,7 @@ export function getAuthUrl(application, provider, method) {
|
|||||||
return `${WeComAuthUri}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&usertype=member`
|
return `${WeComAuthUri}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&usertype=member`
|
||||||
} else if (provider.type === "Lark") {
|
} else if (provider.type === "Lark") {
|
||||||
return `${LarkAuthUri}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`
|
return `${LarkAuthUri}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`
|
||||||
|
} else if (provider.type === "GitLab") {
|
||||||
|
return `${GitLabAuthUri}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${GitLabAuthScope}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user