Skip to content

Commit

Permalink
Merge pull request #28 from moonstream-to/fix-auth-with-res
Browse files Browse the repository at this point in the history
Authorization with access brood resource workflow
  • Loading branch information
kompotkot authored Jan 24, 2024
2 parents a5484b0 + 6b9e550 commit 16f67a6
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 81 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,16 @@ Executes `sign dropper` functionalities such as certifying drop claims for Dropp
With configuration file you can specify list of signers and sign drops with chosen one. Example of server configuration file `config.json`:

```json
[
{
"keyfile_path": "dev.json",
"password": "password.txt",
"password_type": "text_file"
}
]
{
"access_resource_id": "<access_brood_resource_uuid>",
"signers": [
{
"keyfile_path": "dev.json",
"password": "password.txt",
"password_type": "text_file"
}
]
}
```

Config also could be generated with command:
Expand Down
53 changes: 20 additions & 33 deletions bugout.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,62 +215,49 @@ func (c *BugoutAPIClient) GetUser(accessToken string) (User, error) {
return user, nil
}

type AccessWaggleResourceData struct {
Type string `json:"type"`
Customer string `json:"customer"`
AccessLevel string `json:"access_level"`
UserId string `json:"user_id"`
type ResourceHolder struct {
Id string `json:"id"`
HolderType string `json:"holder_type"`
Permissions []string `json:"permissions"`
}

type AccessWaggleResource struct {
Id string `json:"id"`
ApplicationId string `json:"application_id"`
ResourceData AccessWaggleResourceData `json:"resource_data"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
type ResourceHolders struct {
ResourceId string `json:"resource_id"`
Holders []ResourceHolder `json:"holders"`
}

type AccessWaggleResources struct {
Resources []AccessWaggleResource `json:"resources"`
}
func (c *BugoutAPIClient) CheckAccessToResource(token, resourceId string) (ResourceHolders, int, error) {
var resourceHolders ResourceHolders

func (c *BugoutAPIClient) GetAccessLevelFromResources() (AccessWaggleResources, error) {
var accessWaggleResources AccessWaggleResources
var requestBodyBytes []byte
request, requestErr := http.NewRequest("GET", fmt.Sprintf("%s/resources", c.BroodBaseURL), bytes.NewBuffer(requestBodyBytes))
request, requestErr := http.NewRequest("GET", fmt.Sprintf("%s/resources/%s/holders", c.BroodBaseURL, resourceId), bytes.NewBuffer(requestBodyBytes))
if requestErr != nil {
return accessWaggleResources, requestErr
return resourceHolders, 500, requestErr
}
queryParameters := request.URL.Query()
queryParameters.Add("application_id", MOONSTREAM_APPLICATION_ID)
queryParameters.Add("type", BUGOUT_RESOURCE_TYPE_WAGGLE_ACCESS)

request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", MOONSTREAM_WAGGLE_ADMIN_ACCESS_TOKEN))
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
request.Header.Add("Accept", "application/json")
request.Header.Add("Content-Type", "application/json")

response, responseErr := c.HTTPClient.Do(request)
if responseErr != nil {
return accessWaggleResources, responseErr
return resourceHolders, 500, responseErr
}
defer response.Body.Close()

responseBody, responseBodyErr := io.ReadAll(response.Body)

if response.StatusCode < 200 || response.StatusCode >= 300 {
if responseBodyErr != nil {
return accessWaggleResources, fmt.Errorf("unexpected status code: %d -- could not read response body: %s", response.StatusCode, responseBodyErr.Error())
}
if responseBodyErr != nil {
return resourceHolders, response.StatusCode, fmt.Errorf("could not read response body: %s", responseBodyErr.Error())
}

if responseBodyErr != nil {
return accessWaggleResources, fmt.Errorf("could not read response body: %s", responseBodyErr.Error())
if response.StatusCode < 200 || response.StatusCode >= 300 {
return resourceHolders, response.StatusCode, fmt.Errorf("unexpected status code: %d -- could not read response body: %s", response.StatusCode, response.Status)
}

unmarshalErr := json.Unmarshal(responseBody, &accessWaggleResources)
unmarshalErr := json.Unmarshal(responseBody, &resourceHolders)
if unmarshalErr != nil {
return accessWaggleResources, fmt.Errorf("could not parse response body: %s", unmarshalErr.Error())
return resourceHolders, response.StatusCode, fmt.Errorf("could not parse response body: %s", unmarshalErr.Error())
}

return accessWaggleResources, nil
return resourceHolders, response.StatusCode, nil
}
7 changes: 4 additions & 3 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,16 +532,16 @@ func CreateServerCommand() *cobra.Command {
Use: "run",
Short: "Run API server.",
RunE: func(cmd *cobra.Command, args []string) error {
configs, configsErr := ReadServerSignerConfig(config)
config, configsErr := ReadServerConfig(config)
if configsErr != nil {
return configsErr
}
if len(*configs) == 0 {
if len(config.Signers) == 0 {
return fmt.Errorf("no signers available")
}

availableSigners := make(map[string]AvailableSigner)
for _, c := range *configs {
for _, c := range config.Signers {
key, keyErr := KeyFromFile(c.KeyfilePath, c.Password)
if keyErr != nil {
return keyErr
Expand All @@ -567,6 +567,7 @@ func CreateServerCommand() *cobra.Command {
server := Server{
Host: host,
Port: port,
AccessResourceId: config.AccessResourceId,
AvailableSigners: availableSigners,
CORSWhitelist: corsWhitelist,
BugoutAPIClient: bugoutClient,
Expand Down
47 changes: 15 additions & 32 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type AvailableSigner struct {
type Server struct {
Host string
Port int
AccessResourceId string
AvailableSigners map[string]AvailableSigner
LogLevel int
CORSWhitelist map[string]bool
Expand Down Expand Up @@ -75,7 +76,6 @@ type AccessLevel struct {

type AuthorizationContext struct {
AuthorizationToken string
AccessLevel AccessLevel
}

// Check access id was provided correctly and save user access configuration to request context
Expand Down Expand Up @@ -105,46 +105,29 @@ func (server *Server) accessMiddleware(next http.Handler) http.Handler {
return
}

user, getUserErr := server.BugoutAPIClient.GetUser(authorizationToken)
if getUserErr != nil {
log.Println(getUserErr)
http.Error(w, "Access token not found", http.StatusNotFound)
return
}
if user.ApplicationId != MOONSTREAM_APPLICATION_ID {
http.Error(w, "Wrong bugout application", http.StatusForbidden)
return
}

accessWaggleResources, getAccessErr := server.BugoutAPIClient.GetAccessLevelFromResources()
if getAccessErr != nil {
log.Println(getAccessErr)
http.Error(w, "Internal server error", http.StatusInternalServerError)
access, statusCode, checkAccessErr := server.BugoutAPIClient.CheckAccessToResource(authorizationToken, server.AccessResourceId)
if checkAccessErr != nil {
log.Println(statusCode, checkAccessErr)
switch statusCode {
case 404:
http.Error(w, "Not Found", http.StatusNotFound)
case 400, 401, 403:
http.Error(w, "Unauthorized", http.StatusUnauthorized)
default:
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
return
}

var accessLevel AccessLevel
accessGranted := false
for _, resource := range accessWaggleResources.Resources {
if resource.ResourceData.UserId == user.Id {
if resource.ResourceData.AccessLevel == "admin" {
accessLevel.Admin = true
accessGranted = true
}
if resource.ResourceData.AccessLevel == "request_signatures" {
accessLevel.RequestSignatures = true
accessGranted = true
}
}
}
if !accessGranted {
if len(access.Holders) < 1 {
http.Error(w, "Access restricted", http.StatusForbidden)
return
}

// TODO(kompotkot): Add background task to fetch user ID and log it

authorizationContext := AuthorizationContext{
AuthorizationToken: authorizationToken,
AccessLevel: accessLevel,
}

ctxUser := context.WithValue(r.Context(), "authorizationContext", authorizationContext)
Expand Down
18 changes: 12 additions & 6 deletions settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ type ServerSignerConfig struct {
PasswordType string `json:"password_type"`
}

type ServerConfig struct {
AccessResourceId string `json:"access_resource_id"`
Signers []ServerSignerConfig `json:"signers"`
}

// PasswordType specifies available password types
type PasswordType string

Expand Down Expand Up @@ -140,8 +145,8 @@ func (ssc *ServerSignerConfig) ParseKeyfilePassword() (string, error) {
}

// ReadConfig parses list of configuration file paths to list of Application Probes configs
func ReadServerSignerConfig(rawConfigPath string) (*[]ServerSignerConfig, error) {
var configs []ServerSignerConfig
func ReadServerConfig(rawConfigPath string) (*ServerConfig, error) {
var config ServerConfig

configPath := strings.TrimSuffix(rawConfigPath, "/")
_, err := os.Stat(configPath)
Expand All @@ -156,13 +161,14 @@ func ReadServerSignerConfig(rawConfigPath string) (*[]ServerSignerConfig, error)
if err != nil {
log.Fatal(err)
}
configTemp := &[]ServerSignerConfig{}
configTemp := &ServerConfig{}
err = json.Unmarshal(rawBytes, configTemp)
if err != nil {
return nil, err
}

for _, ct := range *configTemp {
config.AccessResourceId = configTemp.AccessResourceId
for _, ct := range configTemp.Signers {
_, err := os.Stat(ct.KeyfilePath)
if err != nil {
if os.IsNotExist(err) {
Expand All @@ -176,12 +182,12 @@ func ReadServerSignerConfig(rawConfigPath string) (*[]ServerSignerConfig, error)
if passParseErr != nil {
continue
}
configs = append(configs, ServerSignerConfig{
config.Signers = append(config.Signers, ServerSignerConfig{
KeyfilePath: ct.KeyfilePath,
Password: password,
PasswordType: ct.PasswordType,
})
}

return &configs, nil
return &config, nil
}

0 comments on commit 16f67a6

Please sign in to comment.