Selukov Andrey
5 years ago
12 changed files with 521 additions and 0 deletions
@ -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"] |
@ -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' |
@ -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) |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 314 KiB |
@ -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 |
||||||
|
} |
@ -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 . |
@ -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…
Reference in new issue