Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
D
dex
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
hdacloud
dex
Commits
1067641e
Commit
1067641e
authored
3 years ago
by
techknowlogick
Browse files
Options
Downloads
Patches
Plain Diff
Feature: groups in Gitea
Signed-off-by:
techknowlogick
<
techknowlogick@gitea.io
>
parent
e9a43bf3
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
README.md
+1
-1
1 addition, 1 deletion
README.md
connector/gitea/gitea.go
+170
-12
170 additions, 12 deletions
connector/gitea/gitea.go
with
171 additions
and
13 deletions
README.md
+
1
−
1
View file @
1067641e
...
...
@@ -79,7 +79,7 @@ Dex implements the following connectors:
|
[
Bitbucket Cloud
](
https://dexidp.io/docs/connectors/bitbucketcloud/
)
| yes | yes | no | alpha | |
|
[
OpenShift
](
https://dexidp.io/docs/connectors/openshift/
)
| no | yes | no | alpha | |
|
[
Atlassian Crowd
](
https://dexidp.io/docs/connectors/atlassiancrowd/
)
| yes | yes | yes
*
| beta | preferred_username claim must be configured through config |
|
[
Gitea
](
https://dexidp.io/docs/connectors/gitea/
)
| yes | no | yes |
alph
a | |
|
[
Gitea
](
https://dexidp.io/docs/connectors/gitea/
)
| yes | no | yes |
bet
a | |
|
[
OpenStack Keystone
](
https://dexidp.io/docs/connectors/keystone/
)
| yes | yes | no | alpha | |
Stable, beta, and alpha are defined as:
...
...
This diff is collapsed.
Click to expand it.
connector/gitea/gitea.go
+
170
−
12
View file @
1067641e
...
...
@@ -20,11 +20,26 @@ import (
// Config holds configuration options for gitea logins.
type
Config
struct
{
BaseURL
string
`json:"baseURL"`
ClientID
string
`json:"clientID"`
ClientSecret
string
`json:"clientSecret"`
RedirectURI
string
`json:"redirectURI"`
UseLoginAsID
bool
`json:"useLoginAsID"`
BaseURL
string
`json:"baseURL"`
ClientID
string
`json:"clientID"`
ClientSecret
string
`json:"clientSecret"`
RedirectURI
string
`json:"redirectURI"`
Orgs
[]
Org
`json:"orgs"`
LoadAllGroups
bool
`json:"loadAllGroups"`
UseLoginAsID
bool
`json:"useLoginAsID"`
}
// Org holds org-team filters, in which teams are optional.
type
Org
struct
{
// Organization name in gitea (not slug, full name). Only users in this gitea
// organization can authenticate.
Name
string
`json:"name"`
// Names of teams in a gitea organization. A user will be able to
// authenticate if they are members of at least one of these teams. Users
// in the organization can authenticate if this field is omitted from the
// config file.
Teams
[]
string
`json:"teams,omitempty"`
}
type
giteaUser
struct
{
...
...
@@ -35,18 +50,20 @@ type giteaUser struct {
IsAdmin
bool
`json:"is_admin"`
}
// Open returns a strategy for logging in through Git
Lab.
// Open returns a strategy for logging in through Git
ea
func
(
c
*
Config
)
Open
(
id
string
,
logger
log
.
Logger
)
(
connector
.
Connector
,
error
)
{
if
c
.
BaseURL
==
""
{
c
.
BaseURL
=
"https://gitea.com"
}
return
&
giteaConnector
{
baseURL
:
c
.
BaseURL
,
redirectURI
:
c
.
RedirectURI
,
clientID
:
c
.
ClientID
,
clientSecret
:
c
.
ClientSecret
,
logger
:
logger
,
useLoginAsID
:
c
.
UseLoginAsID
,
baseURL
:
c
.
BaseURL
,
redirectURI
:
c
.
RedirectURI
,
orgs
:
c
.
Orgs
,
clientID
:
c
.
ClientID
,
clientSecret
:
c
.
ClientSecret
,
logger
:
logger
,
loadAllGroups
:
c
.
LoadAllGroups
,
useLoginAsID
:
c
.
UseLoginAsID
,
},
nil
}
...
...
@@ -64,10 +81,13 @@ var (
type
giteaConnector
struct
{
baseURL
string
redirectURI
string
orgs
[]
Org
clientID
string
clientSecret
string
logger
log
.
Logger
httpClient
*
http
.
Client
// if set to true and no orgs are configured then connector loads all user claims (all orgs and team)
loadAllGroups
bool
// if set to true will use the user's handle rather than their numeric id as the ID
useLoginAsID
bool
}
...
...
@@ -130,6 +150,7 @@ func (c *giteaConnector) HandleCallback(s connector.Scopes, r *http.Request) (id
if
username
==
""
{
username
=
user
.
Email
}
identity
=
connector
.
Identity
{
UserID
:
strconv
.
Itoa
(
user
.
ID
),
Username
:
username
,
...
...
@@ -141,6 +162,15 @@ func (c *giteaConnector) HandleCallback(s connector.Scopes, r *http.Request) (id
identity
.
UserID
=
user
.
Username
}
// Only set identity.Groups if 'orgs', 'org', or 'groups' scope are specified.
if
c
.
groupsRequired
()
{
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
)
if
err
!=
nil
{
return
identity
,
err
}
identity
.
Groups
=
groups
}
if
s
.
OfflineAccess
{
data
:=
connectorData
{
AccessToken
:
token
.
AccessToken
,
...
...
@@ -232,9 +262,132 @@ func (c *giteaConnector) Refresh(ctx context.Context, s connector.Scopes, ident
ident
.
PreferredUsername
=
user
.
Username
ident
.
Email
=
user
.
Email
// Only set identity.Groups if 'orgs', 'org', or 'groups' scope are specified.
if
c
.
groupsRequired
()
{
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
)
if
err
!=
nil
{
return
ident
,
err
}
ident
.
Groups
=
groups
}
return
ident
,
nil
}
// getGroups retrieves Gitea orgs and teams a user is in, if any.
func
(
c
*
giteaConnector
)
getGroups
(
ctx
context
.
Context
,
client
*
http
.
Client
)
([]
string
,
error
)
{
if
len
(
c
.
orgs
)
>
0
{
return
c
.
groupsForOrgs
(
ctx
,
client
)
}
else
if
c
.
loadAllGroups
{
return
c
.
userGroups
(
ctx
,
client
)
}
return
nil
,
nil
}
// formatTeamName returns unique team name.
// Orgs might have the same team names. To make team name unique it should be prefixed with the org name.
func
formatTeamName
(
org
string
,
team
string
)
string
{
return
fmt
.
Sprintf
(
"%s:%s"
,
org
,
team
)
}
// groupsForOrgs returns list of groups that user belongs to in approved list
func
(
c
*
giteaConnector
)
groupsForOrgs
(
ctx
context
.
Context
,
client
*
http
.
Client
)
([]
string
,
error
)
{
groups
,
err
:=
c
.
userGroups
(
ctx
,
client
)
if
err
!=
nil
{
return
groups
,
err
}
keys
:=
make
(
map
[
string
]
bool
)
for
_
,
o
:=
range
c
.
orgs
{
keys
[
o
.
Name
]
=
true
if
o
.
Teams
!=
nil
{
for
_
,
t
:=
range
o
.
Teams
{
keys
[
formatTeamName
(
o
.
Name
,
t
)]
=
true
}
}
}
atLeastOne
:=
false
filteredGroups
:=
make
([]
string
,
0
)
for
_
,
g
:=
range
groups
{
if
_
,
value
:=
keys
[
g
];
value
{
filteredGroups
=
append
(
filteredGroups
,
g
)
atLeastOne
=
true
}
}
if
!
atLeastOne
{
return
[]
string
{},
fmt
.
Errorf
(
"gitea: User does not belong to any of the approved groups"
)
}
return
filteredGroups
,
nil
}
type
organization
struct
{
ID
int64
`json:"id"`
Name
string
`json:"username"`
}
type
team
struct
{
ID
int64
`json:"id"`
Name
string
`json:"name"`
Organization
*
organization
`json:"organization"`
}
func
(
c
*
giteaConnector
)
userGroups
(
ctx
context
.
Context
,
client
*
http
.
Client
)
([]
string
,
error
)
{
apiURL
:=
c
.
baseURL
+
"/api/v1/user/teams"
groups
:=
make
([]
string
,
0
)
page
:=
1
limit
:=
20
for
{
var
teams
[]
team
req
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"%s?page=%d&limit=%d"
,
apiURL
,
page
,
limit
),
nil
)
if
err
!=
nil
{
return
groups
,
fmt
.
Errorf
(
"gitea: new req: %v"
,
err
)
}
req
=
req
.
WithContext
(
ctx
)
resp
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
groups
,
fmt
.
Errorf
(
"gitea: get URL %v"
,
err
)
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
!=
http
.
StatusOK
{
body
,
err
:=
io
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
return
groups
,
fmt
.
Errorf
(
"gitea: read body: %v"
,
err
)
}
return
groups
,
fmt
.
Errorf
(
"%s: %s"
,
resp
.
Status
,
body
)
}
if
err
:=
json
.
NewDecoder
(
resp
.
Body
)
.
Decode
(
&
teams
);
err
!=
nil
{
return
groups
,
fmt
.
Errorf
(
"failed to decode response: %v"
,
err
)
}
if
len
(
teams
)
==
0
{
break
}
for
_
,
t
:=
range
teams
{
groups
=
append
(
groups
,
t
.
Organization
.
Name
)
groups
=
append
(
groups
,
formatTeamName
(
t
.
Organization
.
Name
,
t
.
Name
))
}
page
++
}
// remove duplicate slice variables
keys
:=
make
(
map
[
string
]
struct
{})
list
:=
[]
string
{}
for
_
,
group
:=
range
groups
{
if
_
,
exists
:=
keys
[
group
];
!
exists
{
keys
[
group
]
=
struct
{}{}
list
=
append
(
list
,
group
)
}
}
groups
=
list
return
groups
,
nil
}
// user queries the Gitea API for profile information using the provided client. The HTTP
// client is expected to be constructed by the golang.org/x/oauth2 package, which inserts
// a bearer token as part of the request.
...
...
@@ -264,3 +417,8 @@ func (c *giteaConnector) user(ctx context.Context, client *http.Client) (giteaUs
}
return
u
,
nil
}
// groupsRequired returns whether dex needs to request groups from Gitea.
func
(
c
*
giteaConnector
)
groupsRequired
()
bool
{
return
len
(
c
.
orgs
)
>
0
||
c
.
loadAllGroups
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment