Browse Source

Merge pull request #7 from localleon/config

Config Files
pull/10/head
selukov 4 years ago committed by GitHub
parent
commit
30f12a9ad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      README.md
  2. 20
      config.yaml
  3. 232
      gitea-group-sync.go
  4. 1
      go.mod
  5. 19
      types.go

7
README.md

@ -16,8 +16,15 @@ If you configured the [Gitea](https://hub.docker.com/r/gitea/gitea) <=> [LDAP](h


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


##### Configuration:
There are two ways to configure the application. Via YAML Configuration File or Enviroment Variables.
- See `run.sh` for an example using the enviroment Variables.
- Use `./gitea-group-sync --config="config.yaml"` with the example Config File for the YAML Variant.

##### Gitea Tokens
The application supports several keys, since to add people to the group you must be the owner of the organization. The application supports several keys, since to add people to the group you must be the owner of the organization.



![](images/Image2.png) ![](images/Image2.png)


#### create organizations in gitea #### create organizations in gitea

20
config.yaml

@ -0,0 +1,20 @@
# Example Configuration for gitea-group-sync

ApiKeys:
TokenKey:
- "c00c810bb668c63ce7cd8057411d2f560eac469c,2c02df6959d012dee8f5da3539f63223417c4bbe"
BaseUrl: "http://localhost:3200"

# LDAP Config
LdapURL: "localhost"
LdapPort: 639
LdapTLS: false
LdapBindDN: "cn=admin,dc=planetexpress,dc=com"
LdapBindPassword: "GoodNewsEveryone"
LdapFilter: '(&(objectClass=person)(memberOf=cn=%s,ou=people,dc=planetexpress,dc=com))'
LdapUserSearchBase: 'ou=people,dc=planetexpress,dc=com'
ReqTime: '@every 1m'
LdapUserIdentityAttribute: "uid"
LdapUserFullName: "sn" # can be changed to "cn" if needed


232
gitea-group-sync.go

@ -2,6 +2,7 @@ package main


import ( import (
"crypto/tls" "crypto/tls"
"flag"
"fmt" "fmt"
"log" "log"
"net/url" "net/url"
@ -9,10 +10,11 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
)


import "gopkg.in/ldap.v3" "github.com/robfig/cron/v3"
import "github.com/robfig/cron/v3" "gopkg.in/ldap.v3"
"gopkg.in/yaml.v2"
)


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


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


func main() { var configFlag = flag.String("config", "config.yaml", "Specify YAML Configuration File")


func main() {
// Parse flags of programm
flag.Parse()
mainJob() // First run for check settings mainJob() // First run for check settings


var repTime string var repTime string
@ -66,109 +71,148 @@ func main() {
c.Start() c.Start()
fmt.Println(c.Entries()) fmt.Println(c.Entries())
for true { for true {
time.Sleep(100*time.Second) time.Sleep(100 * time.Second)
} }
} }


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


//------------------------------ // Create temporary structs for creating the final config
// Check and Set input settings envConfig := Config{}
//------------------------------


var apiKeys GiteaKeys // ApiKeys
envConfig.ApiKeys = GiteaKeys{}
envConfig.ApiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
envConfig.ApiKeys.BaseUrl = os.Getenv("GITEA_URL")


if len(os.Getenv("GITEA_TOKEN")) < 40 { // get on https://[web_site_url]/user/settings/applications // LDAP Config
log.Println("GITEA_TOKEN is empty or invalid.") envConfig.LdapURL = os.Getenv("LDAP_URL")
} else { envConfig.LdapBindDN = os.Getenv("BIND_DN")
apiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",") envConfig.LdapBindPassword = os.Getenv("BIND_PASSWORD")
} envConfig.LdapFilter = os.Getenv("LDAP_FILTER")

envConfig.LdapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
if len(os.Getenv("GITEA_URL")) == 0 {
log.Println("GITEA_URL is empty")
} else {
apiKeys.BaseUrl = os.Getenv("GITEA_URL")
}

var ldapUrl string = "ucs.totalwebservices.net"
if len(os.Getenv("LDAP_URL")) == 0 {
log.Println("LDAP_URL is empty")
} else {
ldapUrl = os.Getenv("LDAP_URL")
}


var ldapPort int // Check TLS Settings
var ldapTls bool
if len(os.Getenv("LDAP_TLS_PORT")) > 0 { if len(os.Getenv("LDAP_TLS_PORT")) > 0 {
port, err := strconv.Atoi(os.Getenv("LDAP_TLS_PORT")) port, err := strconv.Atoi(os.Getenv("LDAP_TLS_PORT"))
ldapPort = port envConfig.LdapPort = port
ldapTls = true envConfig.LdapTLS = true
log.Printf("DialTLS:=%v:%d", ldapUrl, ldapPort) log.Printf("DialTLS:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
if err != nil { if err != nil {
log.Println("LDAP_TLS_PORT is invalid.") log.Println("LDAP_TLS_PORT is invalid.")
} }
} else { } else {
if len(os.Getenv("LDAP_PORT")) > 0 { if len(os.Getenv("LDAP_PORT")) > 0 {
port, err := strconv.Atoi(os.Getenv("LDAP_PORT")) port, err := strconv.Atoi(os.Getenv("LDAP_PORT"))
ldapPort = port envConfig.LdapPort = port
ldapTls = false envConfig.LdapTLS = false
log.Printf("Dial:=%v:%d", ldapUrl, ldapPort) log.Printf("Dial:=%v:%d", envConfig.LdapURL, envConfig.LdapPort)
if err != nil { if err != nil {
log.Println("LDAP_PORT is invalid.") log.Println("LDAP_PORT is invalid.")
} }
} }
} }

// Set defaults for user Attributes
var ldapbindDN string if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 {
if len(os.Getenv("BIND_DN")) == 0 { envConfig.LdapUserIdentityAttribute = "uid"
log.Println("BIND_DN is empty") log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
} else { } else {
ldapbindDN = os.Getenv("BIND_DN") envConfig.LdapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
} }


var ldapbindPassword string if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 {
if len(os.Getenv("BIND_PASSWORD")) == 0 { envConfig.LdapUserFullName = "sn" //change to cn if you need it
log.Println("BIND_PASSWORD is empty") log.Println("By default LDAP_USER_FULL_NAME = 'sn'")
} else { } else {
ldapbindPassword = os.Getenv("BIND_PASSWORD") envConfig.LdapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
} }


var ldapUserFilter string return envConfig // return the config struct for use.
if len(os.Getenv("LDAP_FILTER")) == 0 { }
log.Println("LDAP_FILTER is empty")
} else { func importYAMLConfig(path string) (Config, error) {
ldapUserFilter = os.Getenv("LDAP_FILTER") // Open Config File
f, err := os.Open(path)
if err != nil {
return Config{}, err // Aborting
} }
defer f.Close()


var ldapUserSearchBase string // Parse File into Config Struct
if len(os.Getenv("LDAP_USER_SEARCH_BASE")) == 0 { var cfg Config
log.Println("LDAP_USER_SEARCH_BASE is empty") decoder := yaml.NewDecoder(f)
} else { err = decoder.Decode(&cfg)
ldapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE") if err != nil {
return Config{}, err // Aborting
} }
return cfg, nil
}


var ldapUserIdentityAttribute string func (c Config) checkConfig() {
if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 { if len(c.ApiKeys.TokenKey) <= 0 {
ldapUserIdentityAttribute = "uid" log.Println("GITEA_TOKEN is empty or invalid.")
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'") }
if len(c.ApiKeys.BaseUrl) == 0 {
log.Println("GITEA_URL is empty")
}
if len(c.LdapURL) == 0 {
log.Println("LDAP_URL is empty")
}
if c.LdapPort <= 0 {
log.Println("LDAP_TLS_PORT is invalid.")
} else { } else {
ldapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE") log.Printf("DialTLS:=%v:%d", c.LdapURL, c.LdapPort)
} }

if len(c.LdapBindDN) == 0 {
var ldapUserFullName string log.Println("BIND_DN is empty")
if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 { }
ldapUserFullName = "sn" //change to cn if you need it if len(c.LdapBindPassword) == 0 {
log.Println("BIND_PASSWORD is empty")
}
if len(c.LdapFilter) == 0 {
log.Println("LDAP_FILTER is empty")
}
if len(c.LdapUserSearchBase) == 0 {
log.Println("LDAP_USER_SEARCH_BASE is empty")
}
if len(c.LdapUserIdentityAttribute) == 0 {
c.LdapUserIdentityAttribute = "uid"
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
}
if len(c.LdapUserFullName) == 0 {
c.LdapUserFullName = "sn" //change to cn if you need it
log.Println("By default LDAP_USER_FULL_NAME = 'sn'") log.Println("By default LDAP_USER_FULL_NAME = 'sn'")
}
}

func mainJob() {

//------------------------------
// Check and Set input settings
//------------------------------
var cfg Config

cfg, importErr := importYAMLConfig(*configFlag)
if importErr != nil {
log.Println("Fallback: Importing Settings from Enviroment Variables ")
cfg = importEnvVars()
} else { } else {
ldapUserFullName = os.Getenv("LDAP_USER_FULL_NAME") log.Println("Successfully imported YAML Config from " + *configFlag)
fmt.Println(cfg)
} }
// Checks Config
cfg.checkConfig()
log.Println("Checked config elements")


// Prepare LDAP Connection
var l *ldap.Conn var l *ldap.Conn
var err error var err error
if ldapTls { if cfg.LdapTLS {
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapUrl, ldapPort), &tls.Config{InsecureSkipVerify: true}) l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort), &tls.Config{InsecureSkipVerify: true})
} else { } else {
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapUrl, ldapPort)) l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", cfg.LdapURL, cfg.LdapPort))
} }


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


err = l.Bind(ldapbindDN, ldapbindPassword) err = l.Bind(cfg.LdapBindDN, cfg.LdapBindPassword)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
page := 1 page := 1
apiKeys.BruteforceTokenKey = 0 cfg.ApiKeys.BruteforceTokenKey = 0
apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
organizationList := RequestOrganizationList(apiKeys) organizationList := RequestOrganizationList(cfg.ApiKeys)


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


for 1 < len(organizationList) { for 1 < len(organizationList) {


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


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


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


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


// preparing request to ldap server // preparing request to ldap server
filter := fmt.Sprintf(ldapUserFilter, teamList[j].Name) filter := fmt.Sprintf(cfg.LdapFilter, teamList[j].Name)
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
ldapUserSearchBase, // The base dn to search cfg.LdapUserSearchBase, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
filter, // The filter to apply filter, // The filter to apply
[]string{"cn", "uid", "mailPrimaryAddress, sn", ldapUserIdentityAttribute}, // A list attributes to retrieve []string{"cn", "uid", "mailPrimaryAddress, sn", cfg.LdapUserIdentityAttribute}, // A list attributes to retrieve
nil, nil,
) )
// make request to ldap server // make request to ldap server
@ -223,18 +267,18 @@ func mainJob() {
AccountsGitea := make(map[string]Account) AccountsGitea := make(map[string]Account)
var addUserToTeamList, delUserToTeamlist []Account var addUserToTeamList, delUserToTeamlist []Account
if len(sr.Entries) > 0 { if len(sr.Entries) > 0 {
log.Printf("The LDAP %s has %d users corresponding to team %s", ldapUrl, len(sr.Entries), teamList[j].Name) log.Printf("The LDAP %s has %d users corresponding to team %s", cfg.LdapURL, len(sr.Entries), teamList[j].Name)
for _, entry := range sr.Entries { for _, entry := range sr.Entries {


AccountsLdap[entry.GetAttributeValue(ldapUserIdentityAttribute)] = Account{ AccountsLdap[entry.GetAttributeValue(cfg.LdapUserIdentityAttribute)] = Account{
Full_name: entry.GetAttributeValue(ldapUserFullName), Full_name: entry.GetAttributeValue(cfg.LdapUserFullName),
Login: entry.GetAttributeValue(ldapUserIdentityAttribute), Login: entry.GetAttributeValue(cfg.LdapUserIdentityAttribute),
} }
} }


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


for k, v := range AccountsLdap { for k, v := range AccountsLdap {
if AccountsGitea[k].Login != v.Login { if AccountsGitea[k].Login != v.Login {
@ -242,7 +286,7 @@ func mainJob() {
} }
} }
log.Printf("can be added users list %v", addUserToTeamList) log.Printf("can be added users list %v", addUserToTeamList)
AddUsersToTeam(apiKeys, addUserToTeamList, teamList[j].Id) AddUsersToTeam(cfg.ApiKeys, addUserToTeamList, teamList[j].Id)


for k, v := range AccountsGitea { for k, v := range AccountsGitea {
if AccountsLdap[k].Login != v.Login { if AccountsLdap[k].Login != v.Login {
@ -250,18 +294,18 @@ func mainJob() {
} }
} }
log.Printf("must be del users list %v", delUserToTeamlist) log.Printf("must be del users list %v", delUserToTeamlist)
DelUsersFromTeam(apiKeys, delUserToTeamlist, teamList[j].Id) DelUsersFromTeam(cfg.ApiKeys, delUserToTeamlist, teamList[j].Id)


} else { } else {
log.Printf("The LDAP %s not found users corresponding to team %s", ldapUrl, teamList[j].Name) log.Printf("The LDAP %s not found users corresponding to team %s", cfg.LdapURL, teamList[j].Name)
} }
} }
} }


page++ page++
apiKeys.BruteforceTokenKey = 0 cfg.ApiKeys.BruteforceTokenKey = 0
apiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations cfg.ApiKeys.Command = "/api/v1/admin/orgs?page=" + fmt.Sprintf("%d", page) + "&limit=20&access_token=" // List all organizations
organizationList = RequestOrganizationList(apiKeys) organizationList = RequestOrganizationList(cfg.ApiKeys)
log.Printf("%d organizations were found on the server: %s", len(organizationList), apiKeys.BaseUrl) log.Printf("%d organizations were found on the server: %s", len(organizationList), cfg.ApiKeys.BaseUrl)
} }
} }

1
go.mod

@ -5,4 +5,5 @@ go 1.14
require ( require (
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
gopkg.in/ldap.v3 v3.1.0 gopkg.in/ldap.v3 v3.1.0
gopkg.in/yaml.v2 v2.3.0
) )

19
types.go

@ -41,8 +41,23 @@ type SearchResults struct {
} }


type GiteaKeys struct { type GiteaKeys struct {
TokenKey []string TokenKey []string `yaml:"TokenKey"`
BaseUrl string BaseUrl string `yaml:"BaseUrl"`
Command string Command string
BruteforceTokenKey int BruteforceTokenKey int
} }

// Config describes the settings of the application. This structure is used in the settings-import process
type Config struct {
ApiKeys GiteaKeys `yaml:"ApiKeys"`
LdapURL string `yaml:"LdapURL"`
LdapPort int `yaml:"LdapPort"`
LdapTLS bool `yaml:"LdapTLS"`
LdapBindDN string `yaml:"LdapBindDN"`
LdapBindPassword string `yaml:"LdapBindPassword"`
LdapFilter string `yaml:"LdapFilter"`
LdapUserSearchBase string `yaml:"LdapUserSearchBase"`
ReqTime string `yaml:"ReqTime"`
LdapUserIdentityAttribute string `yaml:"LdapUserIdentityAttribute"`
LdapUserFullName string `yaml:"LdapUserFullName"`
} //!TODO! Implement check if valid

Loading…
Cancel
Save