Merge pull request #7 from localleon/config

Config Files
pull/10/head
selukov 2020-10-02 08:04:16 +07:00 committed by GitHub
commit 30f12a9ad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 206 additions and 119 deletions

View File

@ -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 Normal file
View File

@ -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


View File

@ -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
@ -70,105 +75,144 @@ func main() {
} }
} }


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 { // Check TLS Settings
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
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("BIND_DN")) == 0 {
log.Println("BIND_DN is empty")
} else {
ldapbindDN = os.Getenv("BIND_DN")
}

var ldapbindPassword string
if len(os.Getenv("BIND_PASSWORD")) == 0 {
log.Println("BIND_PASSWORD is empty")
} else {
ldapbindPassword = os.Getenv("BIND_PASSWORD")
}

var ldapUserFilter string
if len(os.Getenv("LDAP_FILTER")) == 0 {
log.Println("LDAP_FILTER is empty")
} else {
ldapUserFilter = os.Getenv("LDAP_FILTER")
}

var ldapUserSearchBase string
if len(os.Getenv("LDAP_USER_SEARCH_BASE")) == 0 {
log.Println("LDAP_USER_SEARCH_BASE is empty")
} else {
ldapUserSearchBase = os.Getenv("LDAP_USER_SEARCH_BASE")
}

var ldapUserIdentityAttribute string
if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 { if len(os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")) == 0 {
ldapUserIdentityAttribute = "uid" envConfig.LdapUserIdentityAttribute = "uid"
log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'") log.Println("By default LDAP_USER_IDENTITY_ATTRIBUTE = 'uid'")
} else { } else {
ldapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE") envConfig.LdapUserIdentityAttribute = os.Getenv("LDAP_USER_IDENTITY_ATTRIBUTE")
} }


var ldapUserFullName string
if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 { if len(os.Getenv("LDAP_USER_FULL_NAME")) == 0 {
ldapUserFullName = "sn" //change to cn if you need it envConfig.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'")
} else { } else {
ldapUserFullName = os.Getenv("LDAP_USER_FULL_NAME") envConfig.LdapUserFullName = os.Getenv("LDAP_USER_FULL_NAME")
} }


return envConfig // return the config struct for use.
}

func importYAMLConfig(path string) (Config, error) {
// Open Config File
f, err := os.Open(path)
if err != nil {
return Config{}, err // Aborting
}
defer f.Close()

// Parse File into Config Struct
var cfg Config
decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
return Config{}, err // Aborting
}
return cfg, nil
}

func (c Config) checkConfig() {
if len(c.ApiKeys.TokenKey) <= 0 {
log.Println("GITEA_TOKEN is empty or invalid.")
}
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 {
log.Printf("DialTLS:=%v:%d", c.LdapURL, c.LdapPort)
}
if len(c.LdapBindDN) == 0 {
log.Println("BIND_DN is empty")
}
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'")
}
}

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 {
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
View File

@ -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
) )

View File

@ -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