Skip to content

Commit 22cc747

Browse files
committed
Yaml Config File, Config File Flag and documentation of the feature
1 parent f428ac2 commit 22cc747

File tree

5 files changed

+124
-66
lines changed

5 files changed

+124
-66
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@ If you configured the [Gitea](https://hub.docker.com/r/gitea/gitea) <=> [LDAP](h
1616

1717
You need to create Manage Access Tokens and add key to run.sh or docker-compose.yml the configuration file
1818

19+
##### Configuration:
20+
There are two ways to configure the application. Via YAML Configuration File or Enviroment Variables.
21+
- See `run.sh` for an example using the enviroment Variables.
22+
- Use `./gitea-group-sync --config="config.yaml"` with the example Config File for the YAML Variant.
23+
24+
##### Gitea Tokens
1925
The application supports several keys, since to add people to the group you must be the owner of the organization.
2026

27+
2128
![](images/Image2.png)
2229

2330
#### create organizations in gitea

config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Example Configuration for gitea-group-sync
2+
3+
ApiKeys:
4+
TokenKey:
5+
- "c00c810bb668c63ce7cd8057411d2f560eac469c,2c02df6959d012dee8f5da3539f63223417c4bbe"
6+
BaseUrl: "http://localhost:3200"
7+
8+
# LDAP Config
9+
LdapURL: "localhost"
10+
LdapPort: 639
11+
LdapTLS: false
12+
LdapBindDN: "cn=admin,dc=planetexpress,dc=com"
13+
LdapBindPassword: "GoodNewsEveryone"
14+
LdapFilter: '(&(objectClass=person)(memberOf=cn=%s,ou=people,dc=planetexpress,dc=com))'
15+
LdapUserSearchBase: 'ou=people,dc=planetexpress,dc=com'
16+
ReqTime: '@every 1m'
17+
LdapUserIdentityAttribute: "uid"
18+
LdapUserFullName: "sn" # can be changed to "cn" if needed
19+
20+

gitea-group-sync.go

Lines changed: 79 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"crypto/tls"
5+
"flag"
56
"fmt"
67
"log"
78
"net/url"
@@ -12,6 +13,7 @@ import (
1213

1314
"github.com/robfig/cron/v3"
1415
"gopkg.in/ldap.v3"
16+
"gopkg.in/yaml.v2"
1517
)
1618

1719
func AddUsersToTeam(apiKeys GiteaKeys, users []Account, team int) bool {
@@ -50,8 +52,11 @@ func DelUsersFromTeam(apiKeys GiteaKeys, Users []Account, team int) bool {
5052
return true
5153
}
5254

53-
func main() {
55+
var configFlag = flag.String("config", "config.yaml", "Specify YAML Configuration File")
5456

57+
func main() {
58+
// Parse flags of programm
59+
flag.Parse()
5560
mainJob() // First run for check settings
5661

5762
var repTime string
@@ -70,62 +75,47 @@ func main() {
7075
}
7176
}
7277

73-
// Config describes the settings of the application. This structure is used in the settings-import process
74-
type Config struct {
75-
apiKeys GiteaKeys
76-
ldapUrl string
77-
ldapPort int
78-
ldapTls bool
79-
ldapBindDN string
80-
ldapBindPassword string
81-
ldapFilter string
82-
ldapUserSearchBase string
83-
repTime string
84-
ldapUserIdentityAttribute string
85-
ldapUserFullName string
86-
}
87-
8878
// This Function parses the enviroment for application specific variables and returns a Config struct.
8979
// Used for setting all required settings in the application
9080
func importEnvVars() Config {
9181

9282
// Create temporary structs for creating the final config
9383
envConfig := Config{}
94-
envConfig.apiKeys = GiteaKeys{}
84+
envConfig.ApiKeys = GiteaKeys{}
9585

9686
// Start parsing env. Variables
9787
if len(os.Getenv("GITEA_TOKEN")) < 40 { // get on https://[web_site_url]/user/settings/applications
9888
log.Println("GITEA_TOKEN is empty or invalid.")
9989
} else {
100-
envConfig.apiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
90+
envConfig.ApiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
10191
}
10292

10393
if len(os.Getenv("GITEA_URL")) == 0 {
10494
log.Println("GITEA_URL is empty")
10595
} else {
106-
envConfig.apiKeys.BaseUrl = os.Getenv("GITEA_URL")
96+
envConfig.ApiKeys.BaseUrl = os.Getenv("GITEA_URL")
10797
}
10898

10999
if len(os.Getenv("LDAP_URL")) == 0 {
110100
log.Println("LDAP_URL is empty")
111101
} else {
112-
envConfig.ldapUrl = os.Getenv("LDAP_URL")
102+
envConfig.LdapURL = os.Getenv("LDAP_URL")
113103
}
114104

115105
if len(os.Getenv("LDAP_TLS_PORT")) > 0 {
116106
port, err := strconv.Atoi(os.Getenv("LDAP_TLS_PORT"))
117-
envConfig.ldapPort = port
118-
envConfig.ldapTls = true
119-
log.Printf("DialTLS:=%v:%d", envConfig.ldapUrl, envConfig.ldapPort)
107+
envConfig.LdapPort = port
108+
envConfig.LdapTLS = true
109+
log.Printf("DialTLS:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
120110
if err != nil {
121111
log.Println("LDAP_TLS_PORT is invalid.")
122112
}
123113
} else {
124114
if len(os.Getenv("LDAP_PORT")) > 0 {
125115
port, err := strconv.Atoi(os.Getenv("LDAP_PORT"))
126-
envConfig.ldapPort = port
127-
envConfig.ldapTls = false
128-
log.Printf("Dial:=%v:%d", envConfig.ldapUrl, envConfig.ldapPort)
116+
envConfig.LdapPort = port
117+
envConfig.LdapTLS = false
118+
log.Printf("Dial:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
129119
if err != nil {
130120
log.Println("LDAP_PORT is invalid.")
131121
}
@@ -135,60 +125,85 @@ func importEnvVars() Config {
135125
if len(os.Getenv("BIND_DN")) == 0 {
136126
log.Println("BIND_DN is empty")
137127
} else {
138-
envConfig.ldapBindDN = os.Getenv("BIND_DN")
128+
envConfig.LdapBindDN = os.Getenv("BIND_DN")
139129
}
140130

141131
if len(os.Getenv("BIND_PASSWORD")) == 0 {
142132
log.Println("BIND_PASSWORD is empty")
143133
} else {
144-
envConfig.ldapBindPassword = os.Getenv("BIND_PASSWORD")
134+
envConfig.LdapBindPassword = os.Getenv("BIND_PASSWORD")
145135
}
146136

147137
if len(os.Getenv("LDAP_FILTER")) == 0 {
148138
log.Println("LDAP_FILTER is empty")
149139
} else {
150-
envConfig.ldapFilter = os.Getenv("LDAP_FILTER")
140+
envConfig.LdapFilter = os.Getenv("LDAP_FILTER")
151141
}
152142

153143
if len(os.Getenv("LDAP_USER_SEARCH_BASE")) == 0 {
154144
log.Println("LDAP_USER_SEARCH_BASE is empty")
155145
} else {
156-
envConfig.ldapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
146+
envConfig.LdapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
157147
}
158148

159149
if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 {
160-
envConfig.ldapUserIdentityAttribute = "uid"
150+
envConfig.LdapUserIdentityAttribute = "uid"
161151
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
162152
} else {
163-
envConfig.ldapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
153+
envConfig.LdapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
164154
}
165155

166156
if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 {
167-
envConfig.ldapUserFullName = "sn" //change to cn if you need it
157+
envConfig.LdapUserFullName = "sn" //change to cn if you need it
168158
log.Println("By default LDAP_USER_FULL_NAME = 'sn'")
169159
} else {
170-
envConfig.ldapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
160+
envConfig.LdapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
171161
}
172162

173163
return envConfig // return the config struct for use.
174164
}
175165

166+
func importYAMLConfig(path string) (Config, error) {
167+
// Open Config File
168+
f, err := os.Open(path)
169+
if err != nil {
170+
return Config{}, err // Aborting
171+
}
172+
defer f.Close()
173+
174+
// Parse File into Config Struct
175+
var cfg Config
176+
decoder := yaml.NewDecoder(f)
177+
err = decoder.Decode(&cfg)
178+
if err != nil {
179+
return Config{}, err // Aborting
180+
}
181+
return cfg, nil
182+
}
183+
176184
func mainJob() {
177185

178186
//------------------------------
179187
// Check and Set input settings
180188
//------------------------------
189+
var cfg Config
181190

182-
log.Println("Fallback: Importing Settings from Enviroment Variables ")
183-
cfg := importEnvVars()
191+
cfg, importErr := importYAMLConfig(*configFlag)
192+
if importErr != nil {
193+
log.Println("Fallback: Importing Settings from Enviroment Variables ")
194+
cfg = importEnvVars()
195+
} else {
196+
log.Println("Successfully imported YAML Config from " + *configFlag)
197+
fmt.Println(cfg)
198+
}
184199

185200
// Prepare LDAP Connection
186201
var l *ldap.Conn
187202
var err error
188-
if cfg.ldapTls {
189-
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", cfg.ldapUrl, cfg.ldapPort), &tls.Config{InsecureSkipVerify: true})
203+
if cfg.LdapTLS {
204+
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort), &tls.Config{InsecureSkipVerify: true})
190205
} else {
191-
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", cfg.ldapUrl, cfg.ldapPort))
206+
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort))
192207
}
193208

194209
if err != nil {
@@ -198,16 +213,16 @@ func mainJob() {
198213
}
199214
defer l.Close()
200215

201-
err = l.Bind(cfg.ldapBindDN, cfg.ldapBindPassword)
216+
err = l.Bind(cfg.LdapBindDN, cfg.LdapBindPassword)
202217
if err != nil {
203218
log.Fatal(err)
204219
}
205220
page := 1
206-
cfg.apiKeys.BruteforceTokenKey = 0
207-
cfg.apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
208-
organizationList := RequestOrganizationList(cfg.apiKeys)
221+
cfg.ApiKeys.BruteforceTokenKey = 0
222+
cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
223+
organizationList := RequestOrganizationList(cfg.ApiKeys)
209224

210-
log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.apiKeys.BaseUrl)
225+
log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.ApiKeys.BaseUrl)
211226

212227
for 1 < len(organizationList) {
213228

@@ -217,21 +232,21 @@ func mainJob() {
217232

218233
log.Printf("Begin an organization review: OrganizationName= %v, OrganizationId= %d \n", organizationList[i].Name, organizationList[i].Id)
219234

220-
cfg.apiKeys.Command = "/api/v1/orgs/" + organizationList[i].Name + "/teams?access_token="
221-
teamList := RequestTeamList(cfg.apiKeys)
235+
cfg.ApiKeys.Command = "/api/v1/orgs/" + organizationList[i].Name + "/teams?access_token="
236+
teamList := RequestTeamList(cfg.ApiKeys)
222237
log.Printf("%d teams were found in %s organization", len(teamList), organizationList[i].Name)
223238
log.Printf("Skip synchronization in the Owners team")
224-
cfg.apiKeys.BruteforceTokenKey = 0
239+
cfg.ApiKeys.BruteforceTokenKey = 0
225240

226241
for j := 1; j < len(teamList); j++ {
227242

228243
// preparing request to ldap server
229-
filter := fmt.Sprintf(cfg.ldapFilter, teamList[j].Name)
244+
filter := fmt.Sprintf(cfg.LdapFilter, teamList[j].Name)
230245
searchRequest := ldap.NewSearchRequest(
231-
cfg.ldapUserSearchBase, // The base dn to search
246+
cfg.LdapUserSearchBase, // The base dn to search
232247
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
233248
filter, // The filter to apply
234-
[]string{"cn", "uid", "mailPrimaryAddress, sn", cfg.ldapUserIdentityAttribute}, // A list attributes to retrieve
249+
[]string{"cn", "uid", "mailPrimaryAddress, sn", cfg.LdapUserIdentityAttribute}, // A list attributes to retrieve
235250
nil,
236251
)
237252
// make request to ldap server
@@ -243,45 +258,45 @@ func mainJob() {
243258
AccountsGitea := make(map[string]Account)
244259
var addUserToTeamList, delUserToTeamlist []Account
245260
if len(sr.Entries) > 0 {
246-
log.Printf("The LDAP %s has %d users corresponding to team %s", cfg.ldapUrl, len(sr.Entries), teamList[j].Name)
261+
log.Printf("The LDAP %s has %d users corresponding to team %s", cfg.LdapURL, len(sr.Entries), teamList[j].Name)
247262
for _, entry := range sr.Entries {
248263

249-
AccountsLdap[entry.GetAttributeValue(cfg.ldapUserIdentityAttribute)] = Account{
250-
Full_name: entry.GetAttributeValue(cfg.ldapUserFullName),
251-
Login: entry.GetAttributeValue(cfg.ldapUserIdentityAttribute),
264+
AccountsLdap[entry.GetAttributeValue(cfg.LdapUserIdentityAttribute)] = Account{
265+
Full_name: entry.GetAttributeValue(cfg.LdapUserFullName),
266+
Login: entry.GetAttributeValue(cfg.LdapUserIdentityAttribute),
252267
}
253268
}
254269

255-
cfg.apiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", teamList[j].Id) + "/members?access_token="
256-
AccountsGitea, cfg.apiKeys.BruteforceTokenKey = RequestUsersList(cfg.apiKeys)
257-
log.Printf("The gitea %s has %d users corresponding to team %s Teamid=%d", cfg.apiKeys.BaseUrl, len(AccountsGitea), teamList[j].Name, teamList[j].Id)
270+
cfg.ApiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", teamList[j].Id) + "/members?access_token="
271+
AccountsGitea, cfg.ApiKeys.BruteforceTokenKey = RequestUsersList(cfg.ApiKeys)
272+
log.Printf("The gitea %s has %d users corresponding to team %s Teamid=%d", cfg.ApiKeys.BaseUrl, len(AccountsGitea), teamList[j].Name, teamList[j].Id)
258273

259274
for k, v := range AccountsLdap {
260275
if AccountsGitea[k].Login != v.Login {
261276
addUserToTeamList = append(addUserToTeamList, v)
262277
}
263278
}
264279
log.Printf("can be added users list %v", addUserToTeamList)
265-
AddUsersToTeam(cfg.apiKeys, addUserToTeamList, teamList[j].Id)
280+
AddUsersToTeam(cfg.ApiKeys, addUserToTeamList, teamList[j].Id)
266281

267282
for k, v := range AccountsGitea {
268283
if AccountsLdap[k].Login != v.Login {
269284
delUserToTeamlist = append(delUserToTeamlist, v)
270285
}
271286
}
272287
log.Printf("must be del users list %v", delUserToTeamlist)
273-
DelUsersFromTeam(cfg.apiKeys, delUserToTeamlist, teamList[j].Id)
288+
DelUsersFromTeam(cfg.ApiKeys, delUserToTeamlist, teamList[j].Id)
274289

275290
} else {
276-
log.Printf("The LDAP %s not found users corresponding to team %s", cfg.ldapUrl, teamList[j].Name)
291+
log.Printf("The LDAP %s not found users corresponding to team %s", cfg.LdapURL, teamList[j].Name)
277292
}
278293
}
279294
}
280295

281296
page++
282-
cfg.apiKeys.BruteforceTokenKey = 0
283-
cfg.apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
284-
organizationList = RequestOrganizationList(cfg.apiKeys)
285-
log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.apiKeys.BaseUrl)
297+
cfg.ApiKeys.BruteforceTokenKey = 0
298+
cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
299+
organizationList = RequestOrganizationList(cfg.ApiKeys)
300+
log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.ApiKeys.BaseUrl)
286301
}
287302
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ go 1.14
55
require (
66
github.com/robfig/cron/v3 v3.0.1
77
gopkg.in/ldap.v3 v3.1.0
8+
gopkg.in/yaml.v2 v2.3.0
89
)

types.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,23 @@ type SearchResults struct {
4141
}
4242

4343
type GiteaKeys struct {
44-
TokenKey []string
45-
BaseUrl string
44+
TokenKey []string `yaml:"TokenKey"`
45+
BaseUrl string `yaml:"BaseUrl"`
4646
Command string
4747
BruteforceTokenKey int
4848
}
49+
50+
// Config describes the settings of the application. This structure is used in the settings-import process
51+
type Config struct {
52+
ApiKeys GiteaKeys `yaml:"ApiKeys"`
53+
LdapURL string `yaml:"ldapurl"`
54+
LdapPort int `yaml:"LdapPort"`
55+
LdapTLS bool `yaml:"LdapTLS"`
56+
LdapBindDN string `yaml:"LdapBindDN"`
57+
LdapBindPassword string `yaml:"LdapBindPassword"`
58+
LdapFilter string `yaml:"LdapFilter"`
59+
LdapUserSearchBase string `yaml:"LdapUserSearchBase"`
60+
ReqTime string `yaml:"ReqTime"`
61+
LdapUserIdentityAttribute string `yaml:"LdapUserIdentityAttribute"`
62+
LdapUserFullName string `yaml:"LdapUserFullName"`
63+
}

0 commit comments

Comments
 (0)