Browse Source

have a good time

stable
Selukov Andrey 5 years ago
parent
commit
acc2f12acc
  1. 16
      Dockerfile
  2. 16
      docker-compose.yml
  3. 234
      gitea-group-sync.go
  4. BIN
      images/Image3.png
  5. BIN
      images/Image4.png
  6. BIN
      images/Image5.png
  7. BIN
      images/Image6.png
  8. BIN
      images/Image7.png
  9. BIN
      images/Image8.png
  10. 196
      requests.go
  11. 11
      run.sh
  12. 48
      types.go

16
Dockerfile

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
FROM golang:1.13-alpine3.10 AS build-env

#Setup
COPY . /src/gitea-group-sync
WORKDIR /src/gitea-group-sync

#Build deps
RUN apk --no-cache add build-base git

RUN go get gopkg.in/ldap.v3 && go get gopkg.in/robfig/cron.v3 && go build

FROM alpine:3.10

COPY --from=build-env /src/gitea-group-sync/gitea-group-sync /app/gitea-group-sync/gitea-group-sync
RUN ln -s /app/gitea-group-sync/gitea-group-sync /usr/local/bin/gitea-group-sync
ENTRYPOINT ["/usr/local/bin/gitea-group-sync"]

16
docker-compose.yml

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
version: '3'
services:
group-sync:
container_name: gitea-group-sync
build: .
image: localhost:5000/gitea-group-sync
environment:
GITEA_TOKEN: c00c810bb668c63ce7cd8057411d2f560eac469c
GITEA_URL: http://192.168.2.2:3000
LDAP_URL: 192.168.2.2
LDAP_TLS_PORT: 636
BIND_DN: cn=admin,dc=planetexpress,dc=com
BIND_PASSWORD: GoodNewsEveryone
LDAP_FILTER: (&(objectClass=person)(memberOf=cn=%s,ou=people,dc=planetexpress,dc=com))
LDAP_USER_SEARCH_BASE: 'ou=people,dc=planetexpress,dc=com'
REP_TIME: '@every 1m'

234
gitea-group-sync.go

@ -0,0 +1,234 @@ @@ -0,0 +1,234 @@
package main

import (
"crypto/tls"
"fmt"
"log"
"net/url"
"os"
"strconv"
"strings"
"time"
)

import "gopkg.in/ldap.v3"
import "gopkg.in/robfig/cron.v3"

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

for i := 0; i < len(users); i++ {

fullusername := url.PathEscape(fmt.Sprintf("%s", users[i].Full_name))
apiKeys.Command = "/api/v1/users/search?q=" + fullusername + "&access_token="
foundUsers := RequestSearchResults(apiKeys)

for j := 0; j < len(foundUsers.Data); j++ {

if strings.EqualFold(users[i].Login, foundUsers.Data[j].Login) {
apiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", team) + "/members/" + foundUsers.Data[j].Login + "?access_token="
error := RequestPut(apiKeys)
if len(error) > 0 {
log.Println("Error (Team does not exist or Not Found User) :", parseJson(error).(map[string]interface{})["message"])
}
}
}
}
return true
}

func DelUsersFromTeam(apiKeys GiteaKeys, Users []Account, team int) bool {

for i := 0; i < len(Users); i++ {

apiKeys.Command = "/api/v1/users/search?uid=" + fmt.Sprintf("%d", Users[i].Id) + "&access_token="

foundUser := RequestSearchResults(apiKeys)

apiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", team) + "/members/" + foundUser.Data[0].Login + "?access_token="
RequestDel(apiKeys)
}
return true
}

func main() {

mainJob() // First run for check settings

var repTime string
if len(os.Getenv("REP_TIME")) == 0 {

} else {
repTime = os.Getenv("REP_TIME")
}

c := cron.New()
c.AddFunc(repTime, mainJob)
c.Start()
fmt.Println(c.Entries())
for true {
time.Sleep(100*time.Second)
}
}

func mainJob() {

//------------------------------
// Check and Set input settings
//------------------------------

var apiKeys GiteaKeys

if len(os.Getenv("GITEA_TOKEN")) < 40 { // get on https://[web_site_url]/user/settings/applications
log.Println("GITEA_TOKEN is empty or invalid.")
} else {
apiKeys.TokenKey = strings.Split(os.Getenv("GITEA_TOKEN"), ",")
}

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
if len(os.Getenv("LDAP_TLS_PORT")) > 0 {
port, err := strconv.Atoi(os.Getenv("LDAP_TLS_PORT"))
ldapPort = port
log.Printf("DialTLS:=%v:%d", ldapUrl, ldapPort)
if err != nil {
log.Println("LDAP_TLS_PORT is invalid.")
}
} else {
log.Println("LDAP_TLS_PORT is empty")
}

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")
}

l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapUrl, ldapPort), &tls.Config{InsecureSkipVerify: true})
if err != nil {
fmt.Println(err)
fmt.Println("Please set the correct values for all specifics.")
os.Exit(1)
}
defer l.Close()

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

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

for 1 < len(organizationList) {

for i := 0; i < len(organizationList); i++ {

log.Println(organizationList)

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="
teamList := RequestTeamList(apiKeys)
log.Printf("%d teams were found in %s organization", len(teamList), organizationList[i].Name)
log.Printf("Skip synchronization in the Owners team")
apiKeys.BruteforceTokenKey = 0

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

// preparing request to ldap server
filter := fmt.Sprintf(ldapUserFilter, teamList[j].Name)
searchRequest := ldap.NewSearchRequest(
ldapUserSearchBase, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
filter, // The filter to apply
[]string{"cn", "uid", "mailPrimaryAddress, sn"}, // A list attributes to retrieve
nil,
)
// make request to ldap server
sr, err := l.Search(searchRequest)
if err != nil {
log.Fatal(err)
}
AccountsLdap := make(map[string]Account)
AccountsGitea := make(map[string]Account)
var addUserToTeamList, delUserToTeamlist []Account
if len(sr.Entries) > 0 {
log.Printf("The LDAP %s has %d users corresponding to team %s", ldapUrl, len(sr.Entries), teamList[j].Name)
for _, entry := range sr.Entries {

AccountsLdap[entry.GetAttributeValue("uid")] = Account{
Full_name: entry.GetAttributeValue("sn"), //change to cn if you need it
Login: entry.GetAttributeValue("uid"),
}
}

apiKeys.Command = "/api/v1/teams/" + fmt.Sprintf("%d", teamList[j].Id) + "/members?access_token="
AccountsGitea, apiKeys.BruteforceTokenKey = RequestUsersList(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)

for k, v := range AccountsLdap {
if AccountsGitea[k].Login != v.Login {
addUserToTeamList = append(addUserToTeamList, v)
}
}
log.Printf("can be added users list %v", addUserToTeamList)
AddUsersToTeam(apiKeys, addUserToTeamList, teamList[j].Id)

for k, v := range AccountsGitea {
if AccountsLdap[k].Login != v.Login {
delUserToTeamlist = append(delUserToTeamlist, v)
}
}
log.Printf("must be del users list %v", delUserToTeamlist)
DelUsersFromTeam(apiKeys, delUserToTeamlist, teamList[j].Id)

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

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

BIN
images/Image3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
images/Image4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
images/Image5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
images/Image6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
images/Image7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
images/Image8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

196
requests.go

@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
// "reflect"
"net"
"net/url"
"strings"
"time"
)

func CheckStatusCode(res *http.Response) {

switch {
case 300 <= res.StatusCode && res.StatusCode < 400:
fmt.Println("CheckStatusCode gitea apiKeys connection error: Redirect message")
case 401 == res.StatusCode:
fmt.Println("CheckStatusCode gitea apiKeys connection Error: Unauthorized")
case 400 <= res.StatusCode && res.StatusCode < 500:
fmt.Println("CheckStatusCode gitea apiKeys connection error: Client error")
case 500 <= res.StatusCode && res.StatusCode < 600:
fmt.Println("CheckStatusCode gitea apiKeys connection error Server error")
}
}

func hasTimedOut(err error) bool {
switch err := err.(type) {
case *url.Error:
if err, ok := err.Err.(net.Error); ok && err.Timeout() {
return true
}
case net.Error:
if err.Timeout() {
return true
}
case *net.OpError:
if err.Timeout() {
return true
}
}

errTxt := "use of closed network connection"
if err != nil && strings.Contains(err.Error(), errTxt) {
return true
}

return false
}

func RequestGet(apiKeys GiteaKeys) []byte {
cc := &http.Client{Timeout: time.Second * 2}
url := apiKeys.BaseUrl + apiKeys.Command + apiKeys.TokenKey[apiKeys.BruteforceTokenKey]
res, err := cc.Get(url)
if err != nil && hasTimedOut(err) {
fmt.Println(err)
os.Exit(1)
}
CheckStatusCode(res)
b, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}
res.Body.Close()
return b
}

func RequestPut(apiKeys GiteaKeys) []byte {
cc := &http.Client{Timeout: time.Second * 2}
url := apiKeys.BaseUrl + apiKeys.Command + apiKeys.TokenKey[apiKeys.BruteforceTokenKey]
request, err := http.NewRequest("PUT", url, nil)
res, err := cc.Do(request)
CheckStatusCode(res)
if err != nil && hasTimedOut(err) {
fmt.Println(err)
os.Exit(1)
}
b, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}
res.Body.Close()
return b
}

func RequestDel(apiKeys GiteaKeys) []byte {

cc := &http.Client{Timeout: time.Second * 2}
url := apiKeys.BaseUrl + apiKeys.Command + apiKeys.TokenKey[apiKeys.BruteforceTokenKey]
request, err := http.NewRequest("DELETE", url, nil)
res, err := cc.Do(request)
CheckStatusCode(res)
if err != nil && hasTimedOut(err) {
fmt.Println(err)
os.Exit(1)
}
b, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}
res.Body.Close()
return b
}

func RequestSearchResults(ApiKeys GiteaKeys) SearchResults {

b := RequestGet(ApiKeys)

var f SearchResults
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Fatal(jsonErr)
}
return f
}

func RequestUsersList(ApiKeys GiteaKeys) (map[string]Account, int) {

b := RequestGet(ApiKeys)
var Account_u = make(map[string]Account)

var f []Account
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Println(jsonErr)
if ApiKeys.BruteforceTokenKey == len(ApiKeys.TokenKey)-1 {
log.Println("Token key is unsuitable, call to system administrator ")
} else {
log.Println("Can't get UsersList try another token key")
}
if ApiKeys.BruteforceTokenKey < len(ApiKeys.TokenKey)-1 {
ApiKeys.BruteforceTokenKey++
log.Printf("BruteforceTokenKey=%d", ApiKeys.BruteforceTokenKey)
Account_u, ApiKeys.BruteforceTokenKey = RequestUsersList(ApiKeys)
}
}

for i := 0; i < len(f); i++ {
Account_u[f[i].Login] = Account{
// Email: f[i].Email,
Id: f[i].Id,
Full_name: f[i].Full_name,
Login: f[i].Login,
}
}
return Account_u, ApiKeys.BruteforceTokenKey
}

func RequestOrganizationList(apiKeys GiteaKeys) []Organization {

b := RequestGet(apiKeys)

var f []Organization
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Printf("Please check setting GITEA_TOKEN, GITEA_URL ")
log.Fatal(jsonErr)
}
return f
}

func RequestTeamList(apiKeys GiteaKeys) []Team {

b := RequestGet(apiKeys)

var f []Team
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Fatal(jsonErr)
}
return f
}

func parseJson(b []byte) interface{} {
var f interface{}
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Fatal(jsonErr)
}
m := f.(interface{})
return m
}

func parseJsonArray(b []byte) []interface{} {
var f interface{}
jsonErr := json.Unmarshal(b, &f)
if jsonErr != nil {
log.Fatal(jsonErr)
}
m := f.([]interface{})
return m
}

11
run.sh

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
#!/bin/sh
export GITEA_TOKEN=c00c810bb668c63ce7cd8057411d2f560eac469c,2c02df6959d012dee8f5da3539f63223417c4bbe
export GITEA_URL=http://localhost:3000
export LDAP_URL=localhost
export LDAP_TLS_PORT=636
export BIND_DN='cn=admin,dc=planetexpress,dc=com'
export BIND_PASSWORD=GoodNewsEveryone
export LDAP_FILTER='(&(objectClass=person)(memberOf=cn=%s,ou=people,dc=planetexpress,dc=com))'
export LDAP_USER_SEARCH_BASE='ou=people,dc=planetexpress,dc=com'
export REP_TIME='@every 1m'
go run .

48
types.go

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
package main

type Organization struct {
Id int `json:"id"`
Avatar_url string `json:"avatar_url"`
Description string `json:"description"`
Full_name string `json:"full_name"`
Location string `json:"location"`
Name string `json:"username"`
Visibility string `json:"visibility"`
Website string `json:"website"`
}

type Team struct {
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Permission string `json:"permission"`
}
type User struct {
Id int `json:"id"`
Avatar_url string `json:"avatar_url"`
Created string `json:"created"`
Email string `json:"email"`
Full_name string `json:"full_name"`
Is_admin bool `json:"is_admin"`
Language string `json:"language"`
Last_login string `json:"last_login"`
Login string `json:"login"`
}

type Account struct {
Id int `json:"id"`
Full_name string `json:"full_name"`
Login string `json:"login"`
}

type SearchResults struct {
Data []User `json:"data"`
Ok bool `json:"ok"`
}

type GiteaKeys struct {
TokenKey []string
BaseUrl string
Command string
BruteforceTokenKey int
}
Loading…
Cancel
Save