-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #227 from eurofurence/issue-225-badge-flags-tooling
Issue 225 badge flags tooling
- Loading branch information
Showing
5 changed files
with
658 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ test/contract/*/logs/** | |
attendee-service | ||
main | ||
|
||
api-generator | ||
api-generator | ||
**/config.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"gopkg.in/yaml.v2" | ||
"log" | ||
"os" | ||
"strings" | ||
) | ||
|
||
type Config struct { | ||
Token string `yaml:"idp_token"` // can take from AUTH cookie in regsys for a user that is staff + director | ||
IDPUrl string `yaml:"idp_url"` // base url with no trailing / | ||
Jwt string `yaml:"jwt"` // can take from admin auth in regsys | ||
Auth string `yaml:"auth"` // can take from admin auth in regsys | ||
RegsysUrl string `yaml:"regsys_url"` // base url including context including /attsrv, no trailing / | ||
StaffGroupID string `yaml:"staff_group_id"` // look up in IDP in url | ||
DirectorsGroupID string `yaml:"directors_group_id"` // look up in IDP in url | ||
} | ||
|
||
func (c *Config) validate() error { | ||
if c.Token == "" { | ||
return errors.New("identity provider token empty") | ||
} | ||
if !strings.HasPrefix(c.IDPUrl, "https://") || strings.HasSuffix(c.IDPUrl, "/") { | ||
return errors.New("invalid identity provider url") | ||
} | ||
if jwtParts := strings.Split(c.Jwt, "."); len(jwtParts) != 3 { | ||
return errors.New("invalid jwt cookie, must contain full jwt with all 3 parts") | ||
} | ||
if c.Auth == "" { | ||
return errors.New("invalid auth cookie") | ||
} | ||
if !strings.HasPrefix(c.RegsysUrl, "http") || strings.HasSuffix(c.RegsysUrl, "/") { | ||
return errors.New("invalid regsys base url") | ||
} | ||
if c.StaffGroupID == "" { | ||
return errors.New("staff group id missing") | ||
} | ||
if c.DirectorsGroupID == "" { | ||
return errors.New("directors group id missing") | ||
} | ||
return nil | ||
} | ||
|
||
func loadValidatedConfig() (Config, error) { | ||
log.Println("reading configuration") | ||
|
||
result := Config{} | ||
|
||
yamlFile, err := os.ReadFile("cmd/staffbadges/config.yaml") | ||
if err != nil { | ||
return result, fmt.Errorf("failed to load config.yaml: %s", err.Error()) | ||
} | ||
|
||
if err := yaml.UnmarshalStrict(yamlFile, &result); err != nil { | ||
return result, fmt.Errorf("failed to parse config.yaml: %s", err.Error()) | ||
} | ||
|
||
if err := result.validate(); err != nil { | ||
return result, fmt.Errorf("failed to validate configuration: %s", err.Error()) | ||
} | ||
|
||
log.Println("successfully read and validated configuration") | ||
return result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
) | ||
|
||
type IDPLookupResult struct { | ||
StaffIDs []string | ||
DirectorIDs []string | ||
} | ||
|
||
func lookupUserIDs(config Config) (IDPLookupResult, error) { | ||
result := IDPLookupResult{} | ||
|
||
staffIDs, err := lookupUserIDsForGroup(config.IDPUrl, config.StaffGroupID, config.Token) | ||
if err != nil { | ||
return result, err | ||
} | ||
|
||
directorIDs, err := lookupUserIDsForGroup(config.IDPUrl, config.DirectorsGroupID, config.Token) | ||
if err != nil { | ||
return result, err | ||
} | ||
|
||
// remove staffIDs that are also directors | ||
result.DirectorIDs = make([]string, 0) | ||
for k, _ := range directorIDs { | ||
if _, ok := staffIDs[k]; ok { | ||
log.Printf("removing staff %s who is also director\n", k) | ||
delete(staffIDs, k) | ||
} | ||
result.DirectorIDs = append(result.DirectorIDs, k) | ||
} | ||
|
||
result.StaffIDs = make([]string, 0) | ||
for k, _ := range staffIDs { | ||
result.StaffIDs = append(result.StaffIDs, k) | ||
} | ||
|
||
log.Printf("we now have %d directors and %d staff", len(result.DirectorIDs), len(result.StaffIDs)) | ||
return result, nil | ||
} | ||
|
||
// --- internals --- | ||
|
||
type groupUsersResponse struct { | ||
Data []struct { | ||
GroupId string `json:"group_id"` | ||
UserId string `json:"user_id"` | ||
Level string `json:"level"` | ||
} `json:"data"` | ||
Links struct { | ||
First string `json:"first"` | ||
Last interface{} `json:"last"` | ||
Prev interface{} `json:"prev"` | ||
Next string `json:"next"` // if null, no next page | ||
} `json:"links"` | ||
Meta struct { | ||
CurrentPage int `json:"current_page"` | ||
From int `json:"from"` | ||
Path string `json:"path"` | ||
PerPage int `json:"per_page"` | ||
To int `json:"to"` | ||
} `json:"meta"` | ||
} | ||
|
||
func requestGroupUsers(url string, token string) (groupUsersResponse, error) { | ||
result := groupUsersResponse{} | ||
|
||
request, err := http.NewRequest(http.MethodGet, url, nil) | ||
if err != nil { | ||
return result, fmt.Errorf("error creating request: %s", err.Error()) | ||
} | ||
request.Header.Add("Authorization", "Bearer "+token) | ||
|
||
response, err := http.DefaultClient.Do(request) | ||
if err != nil { | ||
return result, fmt.Errorf("error making request: %s", err.Error()) | ||
} | ||
|
||
defer response.Body.Close() | ||
body, err := io.ReadAll(response.Body) | ||
if err != nil { | ||
return result, fmt.Errorf("error reading body: %s", err.Error()) | ||
} | ||
|
||
err = json.Unmarshal(body, &result) | ||
if err != nil { | ||
return result, fmt.Errorf("error parsing body: %s", err.Error()) | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
func lookupUserIDsForGroup(baseUrl string, grpId string, token string) (map[string]struct{}, error) { | ||
log.Println("querying idp for group " + grpId) | ||
|
||
result := make(map[string]struct{}) | ||
|
||
url := fmt.Sprintf("%s/api/v1/groups/%s/users?page=1", baseUrl, grpId) | ||
response, err := requestGroupUsers(url, token) | ||
if err != nil { | ||
return result, err | ||
} | ||
for _, entry := range response.Data { | ||
result[entry.UserId] = struct{}{} | ||
} | ||
for response.Links.Next != "" { | ||
response, err = requestGroupUsers(response.Links.Next, token) | ||
if err != nil { | ||
return result, err | ||
} | ||
for _, entry := range response.Data { | ||
result[entry.UserId] = struct{}{} | ||
} | ||
} | ||
|
||
log.Printf("successfully read %d member ids in group %s\n", len(result), grpId) | ||
return result, nil | ||
} |
Oops, something went wrong.