Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/highway middleware #800

Merged
merged 6 commits into from
Sep 2, 2023
Merged

Fix/highway middleware #800

merged 6 commits into from
Sep 2, 2023

Conversation

prnk28
Copy link
Contributor

@prnk28 prnk28 commented Sep 2, 2023

=== auto-pr-body ===

Summary
This Pull Request updates client query functions to use the newly added domainproxy, identityproxy, and serviceproxy packages. It adds functions to check alias availability, get a controller account with an address, get a DID document given an alias, and get a service record given an origin. Suggested refactorings are included.

… service proxies

* refactor(auth.go): replace mdw.GetServiceRecord with serviceproxy.GetServiceRecord
* refactor(auth.go): replace mdw.GetEmailRecordCreator with domainproxy.GetEmailRecordCreator
* refactor(auth.go): replace mdw.GetControllerAccount with identityproxy.GetControllerAccount

* chore(params.go): import domainproxy and serviceproxy packages
* fix(params.go): update CheckAliasAvailable function call to use domainproxy.CheckAliasAvailable
* fix(params.go): update GetServiceRecord function call to use serviceproxy.GetServiceRecord
* fix(params.go): update CheckAliasUnavailable function call to use domainproxy.CheckAliasUnavailable

* chore(broadcaster.go): remove unused imports and commented code
* feat(broadcaster.go): remove PublishControllerAccount function
* feat(broadcaster.go): remove CreateOrganizationRecord function

* chore(checker.go): remove unused middleware package
* chore(checker.go): remove unused imports
* chore(checker.go): remove unused functions CheckAliasAvailable and CheckAliasUnavailable

* chore(retriever.go): remove unused imports
* chore(retriever.go): remove unused variables
* chore(retriever.go): remove unused functions
* chore(retriever.go): remove unused package

* chore(verifier.go): import context package
* chore(verifier.go): import domainproxy package
* chore(verifier.go): import domaintypes package
* chore(verifier.go): import identityproxy package
* chore(verifier.go): import identitytypes package
* refactor(verifier.go): remove unused import
* refactor(verifier.go): remove unused code
* refactor(verifier.go): replace GetEmailRecordCreator with domainproxy.GetEmailRecordCreator
* refactor(verifier.go): replace GetControllerAccount with identityproxy.GetControllerAccount
* refactor(verifier.go): replace GetControllerAccount with identityproxy.GetControllerAccount
* refactor(verifier.go): replace VerifySessionJWTClaims with types.VerifySessionJWTClaims

* feat(verifier.go): add PublishControllerAccount function to create and publish a new controller account to the blockchain
* feat(verifier.go): add CreateOrganizationRecord function to create and publish a new organization record to the blockchain

* feat(query.go): add GetEmailRecordCreator function to retrieve the DIDDocument of a given DID or Alias
* feat(query.go): add CheckAliasAvailable function to check if an alias is available
* feat(query.go): add CheckAliasUnavailable function to check if an alias is unavailable

* feat(query.go): add GetControllerAccount function to retrieve the DIDDocument of a given DID or Alias
* feat(query.go): add GetDIDByAlias function to retrieve the DIDDocument of a given DID or Alias

* feat(query.go): add GetServiceRecord function to retrieve the DIDDocument of a given DID or Alias
* feat(query.go): create a gRPC connection to the server using the provided address and insecure transport
* feat(query.go): make a gRPC request to retrieve the ServiceRecord for the given origin
* chore(context.go): rename LocalContext to AppContext
* chore(context.go): update function signature for Context
* chore(context.go): update function signature for HomeDir
* chore(defaults.go): remove unused Masthead constant
* chore(defaults.go): remove unused Init function
* chore(defaults.go): remove unused initSDKConfig function
* chore(defaults.go): remove unused initSonrConfig function
* chore(defaults.go): remove unused default configuration values

* refactor(root.go): remove unused import statement
* feat(root.go): add import statement for sonrdconfig package
* fix(root.go): update Long description to use sonrdconfig.Masthead
* refactor(root.go): remove initSDKConfig and initSonrConfig function calls, replace with sonrdconfig.Init() call
* fix(defaults.go): update default values for node.rpc.host and node.rpc.port
* fix(root.go): set MinGasPrices to "0.00usnr" to allow 0 gas transactions
* fix(root.go): enable iavl fast node for upgraded nodes
* chore(.goreleaser.yaml): update release name template to include Sonr and version identifier
* feat(publish.yml): schedule the workflow to run daily at 9 AM PST
* feat(publish.yml): add step to publish drafts created yesterday
@prnk28 prnk28 enabled auto-merge (squash) September 2, 2023 22:43
@sonarcloud
Copy link

sonarcloud bot commented Sep 2, 2023

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

0.0% 0.0% Coverage
0.0% 0.0% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@prnk28 prnk28 disabled auto-merge September 2, 2023 22:44
@prnk28 prnk28 merged commit 28ba4ed into master Sep 2, 2023
2 of 4 checks passed
@prnk28 prnk28 deleted the fix/highway-middleware branch September 2, 2023 22:44
Copy link

@senior-dev-bot senior-dev-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feedback from Senior Dev Bot

Comment on lines +1 to +48
package domainproxy

import (
"context"
"fmt"

domaintypes "github.com/sonrhq/core/x/domain/types"
"github.com/spf13/viper"
"google.golang.org/grpc"
)

// GetEmailRecordCreator returns the DIDDocument of a given DID or Alias
func GetEmailRecordCreator(ctx context.Context, index string) (string, error) {
addr := fmt.Sprintf("%s:%d", viper.GetString("node.grpc.host"), viper.GetInt("node.grpc.port"))
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
addr,
grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism.
)
if err != nil {
return "", err
}
defer grpcConn.Close()
qc := domaintypes.NewQueryClient(grpcConn)
resp, err := qc.UsernameRecord(ctx, &domaintypes.QueryGetUsernameRecordsRequest{Index: index})
if err != nil {
return "", err
}
return resp.UsernameRecords.Address, nil
}

// CheckAliasAvailable returns the DIDDocument of a given DID or Alias
func CheckAliasAvailable(ctx context.Context, alias string) (bool, error) {
addr, err := GetEmailRecordCreator(ctx, alias)
if err != nil {
return false, err
}
return addr == "", nil
}

// CheckAliasUnavailable returns the DIDDocument of a given DID or Alias
func CheckAliasUnavailable(ctx context.Context, alias string) (bool, error) {
addr, err := GetEmailRecordCreator(ctx, alias)
if err != nil {
return false, err
}
return addr != "", nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

The code changes look good. The new functions CheckAliasAvailable and CheckAliasUnavailable make use of the existing GetEmailRecordCreator function to check if an alias is available or unavailable. The code follows the best practices of using context and handling errors properly.

Comment on lines +1 to +50
package identityproxy

import (
"context"
"fmt"

identitytypes "github.com/sonrhq/core/x/identity/types"
"github.com/spf13/viper"
"google.golang.org/grpc"
)

// GetControllerAccount returns the DIDDocument of a given DID or Alias
func GetControllerAccount(ctx context.Context, address string) (*identitytypes.ControllerAccount, error) {
addr := fmt.Sprintf("%s:%d", viper.GetString("node.grpc.host"), viper.GetInt("node.grpc.port"))
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
addr,
grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism.
)
if err != nil {
return nil, err
}
defer grpcConn.Close()
qc := identitytypes.NewQueryClient(grpcConn)
resp, err := qc.ControllerAccount(ctx, &identitytypes.QueryGetControllerAccountRequest{Address: address})
if err != nil {
return nil, err
}
return &resp.ControllerAccount, nil
}

// GetDIDByAlias returns the DIDDocument of a given DID or Alias
func GetDIDByAlias(ctx context.Context, alias string) (*identitytypes.DIDDocument, error) {
addr := fmt.Sprintf("%s:%d", viper.GetString("node.grpc.host"), viper.GetInt("node.grpc.port"))
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
addr,
grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism.
)
if err != nil {
return nil, err
}
defer grpcConn.Close()
qc := identitytypes.NewQueryClient(grpcConn)
resp, err := qc.DidByAlsoKnownAs(ctx, &identitytypes.QueryDidByAlsoKnownAsRequest{Alias: alias})
if err != nil {
return nil, err
}
return &resp.DidDocument, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good overall. Here are a few suggestions to improve the code:

  1. Import Grouping: Group import statements together for better readability. It is a best practice to have two separate groups for standard library packages and third-party packages.
import (
	"context"
	"fmt"
	
	"google.golang.org/grpc"

	"github.com/spf13/viper"

	identitytypes "github.com/sonrhq/core/x/identity/types"
)
  1. Error Handling: The code should check for errors when dialing the gRPC server and closing the gRPC connection. It's a good practice to handle errors explicitly.
...
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
	addr,
	grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism.
)
if err != nil {
	return nil, fmt.Errorf("unable to connect to gRPC server: %w", err)
}
defer func() {
	if closeErr := grpcConn.Close(); closeErr != nil {
		// Handle error while closing the gRPC connection, if required.
		fmt.Printf("error closing gRPC connection: %v\n", closeErr)
	}
}()
...
  1. Configuration: The code is using viper to retrieve the gRPC server address and port. Make sure that the necessary configuration values (node.grpc.host and node.grpc.port) are set correctly before invoking these functions.

Overall, the code changes are good and follow best practices. However, it's always a good idea to add proper error handling and check the configuration values before using them.

Comment on lines 5 to 13

mdw "github.com/sonrhq/core/internal/highway/middleware"
"github.com/sonrhq/core/internal/highway/types"
domainproxy "github.com/sonrhq/core/x/domain/client/proxy"
identityproxy "github.com/sonrhq/core/x/identity/client/proxy"
serviceproxy "github.com/sonrhq/core/x/service/client/proxy"
)

// RegisterEscrowIdentity returns the credential assertion options to start account login.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good. They add import statements for the domainproxy, identityproxy, and serviceproxy packages. As long as these packages are being used correctly elsewhere in the code, these import statements are necessary for the code to compile and run correctly.

Comment on lines 25 to 31
origin := c.Query("amount")
alias := c.Query("email")

record, err := mdw.GetServiceRecord(origin)
record, err := serviceproxy.GetServiceRecord(c.Request.Context(), origin)
if err != nil {
c.JSON(500, gin.H{"error": err.Error(), "where": "GetServiceRecord"})
return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good overall. However, there are a couple of improvements that can be made:

  1. Error handling: When handling errors, it is best to follow the principle of "fail early, fail loud". In the original code, the error is simply logged and a generic 500 error response is returned. It would be more helpful to provide specific error messages based on the type of error encountered. For example, if the error is due to a service not being available, you can return a more specific error message indicating that the service is unavailable. This would help in troubleshooting and debugging.

  2. Context propagation: The code change introduces passing the request context to the GetServiceRecord function. This is a good practice as it allows for proper context propagation and cancellation. However, it is important to ensure that the context passed is appropriately canceled when the request is aborted. This can be achieved by using methods like WithCancel or WithTimeout on the context.

Comment on lines 65 to 71
attestionResp := c.Query("attestation")
challenge := c.Query("challenge")
// Get the service record from the origin
record, err := mdw.GetServiceRecord(origin)
record, err := serviceproxy.GetServiceRecord(c.Request.Context(), origin)
if err != nil {
c.JSON(404, gin.H{"error": err.Error(), "where": "GetServiceRecord"})
return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good overall. It seems that the GetServiceRecord function has been moved to a new package called serviceproxy and is now being called with an additional context parameter. This change ensures that the function can also work with a context, which is a good practice.

However, I have a few suggestions for improvement:

  1. Error handling: The code currently returns a JSON response with the error message when GetServiceRecord fails. It would be better to use appropriate HTTP status codes for different error scenarios (e.g., 500 for server errors, 404 for not found, etc.) and provide a consistent error response format. You may consider using a centralized error handling mechanism or middleware.
  2. Documentation: Add comments or documentation for functions and important code blocks to improve code readability and maintainability.
  3. Input validation: It would be beneficial to validate the attestation and challenge query parameters before using them to avoid potential security vulnerabilities or issues. Consider adding input validation checks for proper data types and bounds.

Here's an updated version incorporating these suggestions:

attestation := c.Query("attestation")
challenge := c.Query("challenge")

// Validate input parameters
if attestation == "" || challenge == "" {
    c.JSON(400, gin.H{"error": "Invalid request"})
    return
}

// Get the service record from the origin
record, err := serviceproxy.GetServiceRecord(c.Request.Context(), origin)
if err != nil {
    c.JSON(500, gin.H{"error": "Failed to retrieve service record"})
    return
}

These updates improve error handling and input validation while maintaining the overall functionality of the code.

Comment on lines 1 to 18
package middleware

import (
"context"
"fmt"

"github.com/gin-gonic/gin"
"github.com/go-webauthn/webauthn/protocol"

"github.com/sonrhq/core/internal/highway/types"
"github.com/sonrhq/core/pkg/crypto"
"github.com/sonrhq/core/pkg/did/controller"
domainproxy "github.com/sonrhq/core/x/domain/client/proxy"
domaintypes "github.com/sonrhq/core/x/domain/types"
identityproxy "github.com/sonrhq/core/x/identity/client/proxy"
identitytypes "github.com/sonrhq/core/x/identity/types"
servicetypes "github.com/sonrhq/core/x/service/types"
)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the code changes seem to be fine. They include importing relevant packages and adding context to the imports. However, there are a few suggestions that can be made to improve the code:

  1. Order the import statements: It is best practice to order the import statements in a consistent manner. This can be done alphabetically or by grouping similar imports together.

  2. Remove unused import: There is an empty import statement for github.com/sonrhq/core/pkg/crypto that can be removed.

  3. Use shorter import names: The imported packages github.com/sonrhq/core/x/domain/* and github.com/sonrhq/core/x/identity/* are imported using longer names (domainproxy, domaintypes, identityproxy, identitytypes). It would be better to use shorter names that are easier to read and type.

Here's the updated code with the suggested changes:

package middleware

import (
	"context"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/go-webauthn/webauthn/protocol"

	"github.com/sonrhq/core/internal/highway/types"
	"github.com/sonrhq/core/x/domain/client/proxy"
	"github.com/sonrhq/core/x/domain/types"
	"github.com/sonrhq/core/x/identity/client/proxy"
	"github.com/sonrhq/core/x/identity/types"
	servicetypes "github.com/sonrhq/core/x/service/types"
)

// code... (existing code)

Let me know if I can help you with anything else.

Comment on lines 61 to 72

// IssueCredentialAssertionOptions takes a didDocument and serviceRecord in order to create a credential options.
func IssueCredentialAssertionOptions(email string, record *servicetypes.ServiceRecord) (string, protocol.URLEncodedBase64, string, error) {
addr, err := GetEmailRecordCreator(email)
ctx := context.Background()
addr, err := domainproxy.GetEmailRecordCreator(ctx, email)
if err != nil {
return "", nil, "", fmt.Errorf("failed to get email record creator: %w", err)
}
controllerAcc, err := GetControllerAccount(addr)
controllerAcc, err := identityproxy.GetControllerAccount(ctx, addr)
if err != nil {
return "", nil, "", fmt.Errorf("failed to get controller account: %w", err)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in this code appear to introduce the use of a context and replace the function calls GetEmailRecordCreator and GetControllerAccount with functions from different packages (GetEmailRecordCreator with domainproxy.GetEmailRecordCreator and GetControllerAccount with identityproxy.GetControllerAccount). However, the context is not being used in the new function calls, which can lead to potential issues.

Here are a few suggestions to improve the code:

  1. Make sure to pass the context to the new function calls domainproxy.GetEmailRecordCreator and identityproxy.GetControllerAccount.
  2. Consider using more descriptive variable names for the context and the new function calls, to improve code readability.
  3. Add error handling for the new function calls, as they may also return errors that need to be handled properly.

Updated code:

func IssueCredentialAssertionOptions(email string, record *servicetypes.ServiceRecord) (string, protocol.URLEncodedBase64, string, error) {
    ctx := context.Background()
    emailRecordCreator, err := domainproxy.GetEmailRecordCreator(ctx, email)
    if err != nil {
        return "", nil, "", fmt.Errorf("failed to get email record creator: %w", err)
    }
    controllerAcc, err := identityproxy.GetControllerAccount(ctx, emailRecordCreator)
    if err != nil {
        return "", nil, "", fmt.Errorf("failed to get controller account: %w", err)
    }
    // ... rest of the code
}

The updated code now correctly passes the ctx context to the new function calls and uses more meaningful variable names (emailRecordCreator) to improve code clarity. Additionally, error handling is included for the new function calls.

Comment on lines 97 to 108

// UseControllerAccount takes a jwt token and returns a controller account.
func UseControllerAccount(token string) (*controller.SonrController, error) {
ctx := context.Background()
claims, err := types.VerifySessionJWTClaims(token)
if err != nil {
return nil, fmt.Errorf("failed to verify claims: %w", err)
}
acc, err := GetControllerAccount(claims.Address)
acc, err := identityproxy.GetControllerAccount(ctx, claims.Address)
if err != nil {
return nil, fmt.Errorf("failed to get controller account: %w", err)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good. Here are a few suggestions to further improve the code:

  1. It's a good practice to create a context with a timeout or deadline instead of using context.Background() directly. This can prevent potential resource leaks and help with controlling the execution time of operations.
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) // Add timeout of 5 seconds
defer cancel() // Cancel the context to release resources
  1. Ensure that the context is passed down the call stack to all the relevant functions that might need it. This ensures that the context's timeout or deadline is honored throughout the process.

Here's the modified code incorporating the changes:

func UseControllerAccount(token string) (*controller.SonrController, error) {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) // Add timeout of 5 seconds
    defer cancel() // Cancel the context to release resources

    claims, err := types.VerifySessionJWTClaims(token)
    if err != nil {
        return nil, fmt.Errorf("failed to verify claims: %w", err)
    }
    acc, err := identityproxy.GetControllerAccount(ctx, claims.Address)
    if err != nil {
        return nil, fmt.Errorf("failed to get controller account: %w", err)
    }
    
    // rest of the code...
}
  1. If the GetControllerAccount function can also be modified to accept a context, it would be ideal to pass the existing ctx to it instead of context.Background(). This allows for better control and cancellation of the entire operation.

Overall, the code changes are good, but incorporating the suggestions above can further enhance the code.

Comment on lines 112 to +137
}
return controller, nil
}

// PublishControllerAccount creates a new controller account and publishes it to the blockchain
func PublishControllerAccount(alias string, cred *servicetypes.WebauthnCredential, origin string) (*controller.SonrController, *types.TxResponse, error) {
controller, err := controller.New(alias, cred, origin)
if err != nil {
return nil, nil, fmt.Errorf("failed to create controller: %w", err)

}
acc := controller.Account()
accMsg := identitytypes.NewMsgCreateControllerAccount(acc.Address, acc.PublicKey, acc.Authenticators...)
usrMsg := domaintypes.NewMsgCreateEmailUsernameRecord(acc.Address, alias)
resp, err := controller.GetPrimaryWallet().SendTx(accMsg, usrMsg)
if err != nil {
return nil, nil, fmt.Errorf("failed to send tx: %w", err)
}
fmt.Println(resp)
return controller, resp, nil
}

// CreateOrganizationRecord creates a new organization record and publishes it to the blockchain
func CreateOrganizationRecord(name string, origin string, admin string, controller *controller.SonrController) error {
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good overall. However, the CreateOrganizationRecord function is incomplete and does not have any logic. It currently returns nil without performing any operations. Please ensure that the function performs the necessary actions to create and publish an organization record to the blockchain.

Also, it would be a good practice to handle the error returned by the CreateOrganizationRecord function and provide meaningful error messages to the caller instead of returning nil.

Comment on lines +1 to +18
name: Publish Releases

on:
schedule:
- cron: "0 17 * * *" # 9 AM PST is 17:00 UTC

jobs:
publish-drafts:
runs-on: ubuntu-latest

steps:
- name: Publish Drafts
run: |
yesterday=$(date -u -d "yesterday" +"%Y-%m-%d")
drafts=$(gh release list --json createdAt,draft -q "map(select(.draft==true and .createdAt >= '$yesterday')) | .[].name")
for draft in $drafts; do
gh release update $draft --draft false
done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code changes look good overall. However, there are a few suggestions for improvement:

  1. Add a description: It's good practice to add a brief description to the workflow file to explain what it does and provide context. This can be done by adding a description field under the name field.

  2. Specify the timezone: The cron expression is using UTC time, but it's important to specify the timezone explicitly to avoid confusion. You can add a timezone field under the schedule field and specify the timezone, for example: timezone: America/Los_Angeles.

  3. Use double quotes: The run command is using single quotes for the yesterday variable and inside the gh release list command. It's generally recommended to use double quotes unless there is a specific reason to use single quotes. Double quotes allow for variable expansion and better readability. You can update the code to use double quotes instead.

  4. Handle date formatting: The date command used to calculate yesterday's date might not work on all systems. It would be better to use a more portable and reliable approach. You can use the date command with the -v option, which is available on macOS and some Linux distributions. Alternatively, you can use a programming language like Python or Node.js to calculate the date.

  5. Error handling: Currently, there is no error handling in the code. If there are any errors while executing the gh command, it will fail silently. It would be beneficial to add error handling and logging to ensure that any issues are identified and addressed.

Here's an updated version of the code that incorporates these improvements:

name: Publish Releases
description: Automatically publishes draft releases

on:
  schedule:
    - cron: "0 17 * * *"
  timezone: America/Los_Angeles

jobs:
  publish-drafts:
    runs-on: ubuntu-latest

    steps:
      - name: Publish Drafts
        run: |
          yesterday=$(date -u --date='-1 day' +"%Y-%m-%d")
          drafts=$(gh release list --json createdAt,draft -q "map(select(.draft==true and .createdAt >= \"$yesterday\")) | .[].name")
          for draft in $drafts; do
            gh release update $draft --draft false || echo "Failed to update release: $draft"
          done

Note: The exact method for handling dates may vary depending on the system and tools available. Please adjust the date calculation based on the platform you are using.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

1 participant