Skip to content

Commit 30f12a9

Browse files
authored
Merge pull request gitea-group-sync#7 from localleon/config
Config Files
2 parents 92cd9ec + cb5d805 commit 30f12a9

File tree

5 files changed

+183
-96
lines changed

5 files changed

+183
-96
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: 138 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ package main
22

33
import (
44
"crypto/tls"
5+
"flag"
56
"fmt"
67
"log"
78
"net/url"
89
"os"
910
"strconv"
1011
"strings"
1112
"time"
12-
)
1313

14-
import "gopkg.in/ldap.v3"
15-
import "github.com/robfig/cron/v3"
14+
"github.com/robfig/cron/v3"
15+
"gopkg.in/ldap.v3"
16+
"gopkg.in/yaml.v2"
17+
)
1618

1719
func AddUsersToTeam(apiKeys GiteaKeys, users []Account, team int) bool {
1820

@@ -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
@@ -66,109 +71,148 @@ func main() {
6671
c.Start()
6772
fmt.Println(c.Entries())
6873
for true {
69-
time.Sleep(100*time.Second)
74+
time.Sleep(100 * time.Second)
7075
}
7176
}
7277

73-
func mainJob() {
78+
// This Function parses the enviroment for application specific variables and returns a Config struct.
79+
// Used for setting all required settings in the application
80+
func importEnvVars() Config {
7481

75-
//------------------------------
76-
// Check and Set input settings
77-
//------------------------------
82+
// Create temporary structs for creating the final config
83+
envConfig := Config{}
7884

79-
var apiKeys GiteaKeys
85+
// ApiKeys
86+
envConfig.ApiKeys = GiteaKeys{}
87+
envConfig.ApiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
88+
envConfig.ApiKeys.BaseUrl = os.Getenv("GITEA_URL")
8089

81-
if len(os.Getenv("GITEA_TOKEN")) < 40 { // get on https://[web_site_url]/user/settings/applications
82-
log.Println("GITEA_TOKEN is empty or invalid.")
83-
} else {
84-
apiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
85-
}
86-
87-
if len(os.Getenv("GITEA_URL")) == 0 {
88-
log.Println("GITEA_URL is empty")
89-
} else {
90-
apiKeys.BaseUrl = os.Getenv("GITEA_URL")
91-
}
90+
// LDAP Config
91+
envConfig.LdapURL = os.Getenv("LDAP_URL")
92+
envConfig.LdapBindDN = os.Getenv("BIND_DN")
93+
envConfig.LdapBindPassword = os.Getenv("BIND_PASSWORD")
94+
envConfig.LdapFilter = os.Getenv("LDAP_FILTER")
95+
envConfig.LdapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
9296

93-
var ldapUrl string = "ucs.totalwebservices.net"
94-
if len(os.Getenv("LDAP_URL")) == 0 {
95-
log.Println("LDAP_URL is empty")
96-
} else {
97-
ldapUrl = os.Getenv("LDAP_URL")
98-
}
99-
100-
var ldapPort int
101-
var ldapTls bool
97+
// Check TLS Settings
10298
if len(os.Getenv("LDAP_TLS_PORT")) > 0 {
10399
port, err := strconv.Atoi(os.Getenv("LDAP_TLS_PORT"))
104-
ldapPort = port
105-
ldapTls = true
106-
log.Printf("DialTLS:=%v:%d", ldapUrl, ldapPort)
100+
envConfig.LdapPort = port
101+
envConfig.LdapTLS = true
102+
log.Printf("DialTLS:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
107103
if err != nil {
108104
log.Println("LDAP_TLS_PORT is invalid.")
109105
}
110106
} else {
111107
if len(os.Getenv("LDAP_PORT")) > 0 {
112108
port, err := strconv.Atoi(os.Getenv("LDAP_PORT"))
113-
ldapPort = port
114-
ldapTls = false
115-
log.Printf("Dial:=%v:%d", ldapUrl, ldapPort)
109+
envConfig.LdapPort = port
110+
envConfig.LdapTLS = false
111+
log.Printf("Dial:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
116112
if err != nil {
117113
log.Println("LDAP_PORT is invalid.")
118114
}
119115
}
120-
}
121-
122-
var ldapbindDN string
123-
if len(os.Getenv("BIND_DN")) == 0 {
124-
log.Println("BIND_DN is empty")
116+
}
117+
// Set defaults for user Attributes
118+
if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 {
119+
envConfig.LdapUserIdentityAttribute = "uid"
120+
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
125121
} else {
126-
ldapbindDN = os.Getenv("BIND_DN")
122+
envConfig.LdapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
127123
}
128124

129-
var ldapbindPassword string
130-
if len(os.Getenv("BIND_PASSWORD")) == 0 {
131-
log.Println("BIND_PASSWORD is empty")
125+
if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 {
126+
envConfig.LdapUserFullName = "sn" //change to cn if you need it
127+
log.Println("By default LDAP_USER_FULL_NAME = 'sn'")
132128
} else {
133-
ldapbindPassword = os.Getenv("BIND_PASSWORD")
129+
envConfig.LdapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
134130
}
135131

136-
var ldapUserFilter string
137-
if len(os.Getenv("LDAP_FILTER")) == 0 {
138-
log.Println("LDAP_FILTER is empty")
139-
} else {
140-
ldapUserFilter = os.Getenv("LDAP_FILTER")
132+
return envConfig // return the config struct for use.
133+
}
134+
135+
func importYAMLConfig(path string) (Config, error) {
136+
// Open Config File
137+
f, err := os.Open(path)
138+
if err != nil {
139+
return Config{}, err // Aborting
141140
}
141+
defer f.Close()
142142

143-
var ldapUserSearchBase string
144-
if len(os.Getenv("LDAP_USER_SEARCH_BASE")) == 0 {
145-
log.Println("LDAP_USER_SEARCH_BASE is empty")
146-
} else {
147-
ldapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
143+
// Parse File into Config Struct
144+
var cfg Config
145+
decoder := yaml.NewDecoder(f)
146+
err = decoder.Decode(&cfg)
147+
if err != nil {
148+
return Config{}, err // Aborting
148149
}
150+
return cfg, nil
151+
}
149152

150-
var ldapUserIdentityAttribute string
151-
if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 {
152-
ldapUserIdentityAttribute = "uid"
153-
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
153+
func (c Config) checkConfig() {
154+
if len(c.ApiKeys.TokenKey) <= 0 {
155+
log.Println("GITEA_TOKEN is empty or invalid.")
156+
}
157+
if len(c.ApiKeys.BaseUrl) == 0 {
158+
log.Println("GITEA_URL is empty")
159+
}
160+
if len(c.LdapURL) == 0 {
161+
log.Println("LDAP_URL is empty")
162+
}
163+
if c.LdapPort <= 0 {
164+
log.Println("LDAP_TLS_PORT is invalid.")
154165
} else {
155-
ldapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
166+
log.Printf("DialTLS:=%v:%d", c.LdapURL, c.LdapPort)
156167
}
157-
158-
var ldapUserFullName string
159-
if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 {
160-
ldapUserFullName = "sn" //change to cn if you need it
168+
if len(c.LdapBindDN) == 0 {
169+
log.Println("BIND_DN is empty")
170+
}
171+
if len(c.LdapBindPassword) == 0 {
172+
log.Println("BIND_PASSWORD is empty")
173+
}
174+
if len(c.LdapFilter) == 0 {
175+
log.Println("LDAP_FILTER is empty")
176+
}
177+
if len(c.LdapUserSearchBase) == 0 {
178+
log.Println("LDAP_USER_SEARCH_BASE is empty")
179+
}
180+
if len(c.LdapUserIdentityAttribute) == 0 {
181+
c.LdapUserIdentityAttribute = "uid"
182+
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
183+
}
184+
if len(c.LdapUserFullName) == 0 {
185+
c.LdapUserFullName = "sn" //change to cn if you need it
161186
log.Println("By default LDAP_USER_FULL_NAME = 'sn'")
187+
}
188+
}
189+
190+
func mainJob() {
191+
192+
//------------------------------
193+
// Check and Set input settings
194+
//------------------------------
195+
var cfg Config
196+
197+
cfg, importErr := importYAMLConfig(*configFlag)
198+
if importErr != nil {
199+
log.Println("Fallback: Importing Settings from Enviroment Variables ")
200+
cfg = importEnvVars()
162201
} else {
163-
ldapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
202+
log.Println("Successfully imported YAML Config from " + *configFlag)
203+
fmt.Println(cfg)
164204
}
205+
// Checks Config
206+
cfg.checkConfig()
207+
log.Println("Checked config elements")
165208

209+
// Prepare LDAP Connection
166210
var l *ldap.Conn
167211
var err error
168-
if ldapTls {
169-
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapUrl, ldapPort), &tls.Config{InsecureSkipVerify: true})
212+
if cfg.LdapTLS {
213+
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort), &tls.Config{InsecureSkipVerify: true})
170214
} else {
171-
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapUrl, ldapPort))
215+
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort))
172216
}
173217

174218
if err != nil {
@@ -178,16 +222,16 @@ func mainJob() {
178222
}
179223
defer l.Close()
180224

181-
err = l.Bind(ldapbindDN, ldapbindPassword)
225+
err = l.Bind(cfg.LdapBindDN, cfg.LdapBindPassword)
182226
if err != nil {
183227
log.Fatal(err)
184228
}
185229
page := 1
186-
apiKeys.BruteforceTokenKey = 0
187-
apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
188-
organizationList := RequestOrganizationList(apiKeys)
230+
cfg.ApiKeys.BruteforceTokenKey = 0
231+
cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
232+
organizationList := RequestOrganizationList(cfg.ApiKeys)
189233

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

192236
for 1 < len(organizationList) {
193237

@@ -197,21 +241,21 @@ func mainJob() {
197241

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

200-
apiKeys.Command = "/api/v1/orgs/" + organizationList[i].Name + "/teams?access_token="
201-
teamList := RequestTeamList(apiKeys)
244+
cfg.ApiKeys.Command = "/api/v1/orgs/" + organizationList[i].Name + "/teams?access_token="
245+
teamList := RequestTeamList(cfg.ApiKeys)
202246
log.Printf("%d teams were found in %s organization", len(teamList), organizationList[i].Name)
203247
log.Printf("Skip synchronization in the Owners team")
204-
apiKeys.BruteforceTokenKey = 0
248+
cfg.ApiKeys.BruteforceTokenKey = 0
205249

206250
for j := 1; j < len(teamList); j++ {
207251

208252
// preparing request to ldap server
209-
filter := fmt.Sprintf(ldapUserFilter, teamList[j].Name)
253+
filter := fmt.Sprintf(cfg.LdapFilter, teamList[j].Name)
210254
searchRequest := ldap.NewSearchRequest(
211-
ldapUserSearchBase, // The base dn to search
255+
cfg.LdapUserSearchBase, // The base dn to search
212256
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
213257
filter, // The filter to apply
214-
[]string{"cn", "uid", "mailPrimaryAddress, sn", ldapUserIdentityAttribute}, // A list attributes to retrieve
258+
[]string{"cn", "uid", "mailPrimaryAddress, sn", cfg.LdapUserIdentityAttribute}, // A list attributes to retrieve
215259
nil,
216260
)
217261
// make request to ldap server
@@ -223,45 +267,45 @@ func mainJob() {
223267
AccountsGitea := make(map[string]Account)
224268
var addUserToTeamList, delUserToTeamlist []Account
225269
if len(sr.Entries) > 0 {
226-
log.Printf("The LDAP %s has %d users corresponding to team %s", ldapUrl, len(sr.Entries), teamList[j].Name)
270+
log.Printf("The LDAP %s has %d users corresponding to team %s", cfg.LdapURL, len(sr.Entries), teamList[j].Name)
227271
for _, entry := range sr.Entries {
228272

229-
AccountsLdap[entry.GetAttributeValue(ldapUserIdentityAttribute)] = Account{
230-
Full_name: entry.GetAttributeValue(ldapUserFullName),
231-
Login: entry.GetAttributeValue(ldapUserIdentityAttribute),
273+
AccountsLdap[entry.GetAttributeValue(cfg.LdapUserIdentityAttribute)] = Account{
274+
Full_name: entry.GetAttributeValue(cfg.LdapUserFullName),
275+
Login: entry.GetAttributeValue(cfg.LdapUserIdentityAttribute),
232276
}
233277
}
234278

235-
apiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", teamList[j].Id) + "/members?access_token="
236-
AccountsGitea, apiKeys.BruteforceTokenKey = RequestUsersList(apiKeys)
237-
log.Printf("The gitea %s has %d users corresponding to team %s Teamid=%d", apiKeys.BaseUrl, len(AccountsGitea), teamList[j].Name, teamList[j].Id)
279+
cfg.ApiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", teamList[j].Id) + "/members?access_token="
280+
AccountsGitea, cfg.ApiKeys.BruteforceTokenKey = RequestUsersList(cfg.ApiKeys)
281+
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)
238282

239283
for k, v := range AccountsLdap {
240284
if AccountsGitea[k].Login != v.Login {
241285
addUserToTeamList = append(addUserToTeamList, v)
242286
}
243287
}
244288
log.Printf("can be added users list %v", addUserToTeamList)
245-
AddUsersToTeam(apiKeys, addUserToTeamList, teamList[j].Id)
289+
AddUsersToTeam(cfg.ApiKeys, addUserToTeamList, teamList[j].Id)
246290

247291
for k, v := range AccountsGitea {
248292
if AccountsLdap[k].Login != v.Login {
249293
delUserToTeamlist = append(delUserToTeamlist, v)
250294
}
251295
}
252296
log.Printf("must be del users list %v", delUserToTeamlist)
253-
DelUsersFromTeam(apiKeys, delUserToTeamlist, teamList[j].Id)
297+
DelUsersFromTeam(cfg.ApiKeys, delUserToTeamlist, teamList[j].Id)
254298

255299
} else {
256-
log.Printf("The LDAP %s not found users corresponding to team %s", ldapUrl, teamList[j].Name)
300+
log.Printf("The LDAP %s not found users corresponding to team %s", cfg.LdapURL, teamList[j].Name)
257301
}
258302
}
259303
}
260304

261305
page++
262-
apiKeys.BruteforceTokenKey = 0
263-
apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
264-
organizationList = RequestOrganizationList(apiKeys)
265-
log.Printf("%d organizations were found on the server: %s", len(organizationList), apiKeys.BaseUrl)
306+
cfg.ApiKeys.BruteforceTokenKey = 0
307+
cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
308+
organizationList = RequestOrganizationList(cfg.ApiKeys)
309+
log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.ApiKeys.BaseUrl)
266310
}
267311
}

0 commit comments

Comments
 (0)