diff --git a/api/api.go b/api/api.go index 7e99fa93..34609a00 100644 --- a/api/api.go +++ b/api/api.go @@ -57,27 +57,11 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons return nil, fmt.Errorf("invalid expiresAt: %v", err) } - // request methods are a space separated list of known request kinds TODO: it should be a string array in the API - requestMethods := strings.Split(createAppRequest.RequestMethods, " ") - if len(requestMethods) == 0 { - return nil, fmt.Errorf("won't create an app without request methods") + if len(createAppRequest.Scopes) == 0 { + return nil, fmt.Errorf("won't create an app without scopes") } - for _, m := range requestMethods { - //if we don't support this method, we return an error - if !strings.Contains(api.svc.GetLNClient().GetSupportedNIP47Methods(), m) { - return nil, fmt.Errorf("did not recognize request method: %s", m) - } - } - - for _, m := range notificationTypes { - //if we don't support this method, we return an error - if !strings.Contains(api.svc.GetLNClient().GetSupportedNIP47Methods(), m) { - return nil, fmt.Errorf("did not recognize request method: %s", m) - } - } - - app, pairingSecretKey, err := api.dbSvc.CreateApp(createAppRequest.Name, createAppRequest.Pubkey, createAppRequest.MaxAmount, createAppRequest.BudgetRenewal, expiresAt, requestMethods) + app, pairingSecretKey, err := api.dbSvc.CreateApp(createAppRequest.Name, createAppRequest.Pubkey, createAppRequest.MaxAmount, createAppRequest.BudgetRenewal, expiresAt, createAppRequest.Scopes) if err != nil { return nil, err @@ -116,11 +100,10 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e maxAmount := updateAppRequest.MaxAmount budgetRenewal := updateAppRequest.BudgetRenewal - requestMethods := updateAppRequest.RequestMethods - if requestMethods == "" { + if len(updateAppRequest.Scopes) == 0 { return fmt.Errorf("won't update an app to have no request methods") } - newRequestMethods := strings.Split(requestMethods, " ") + newScopes := updateAppRequest.Scopes expiresAt, err := api.parseExpiresAt(updateAppRequest.ExpiresAt) if err != nil { @@ -143,17 +126,17 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e return err } - existingMethodMap := make(map[string]bool) + existingScopeMap := make(map[string]bool) for _, perm := range existingPermissions { - existingMethodMap[perm.RequestMethod] = true + existingScopeMap[perm.Scope] = true } // Add new permissions - for _, method := range newRequestMethods { - if !existingMethodMap[method] { + for _, method := range newScopes { + if !existingScopeMap[method] { perm := db.AppPermission{ App: *userApp, - RequestMethod: method, + Scope: method, ExpiresAt: expiresAt, MaxAmount: int(maxAmount), BudgetRenewal: budgetRenewal, @@ -162,12 +145,12 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e return err } } - delete(existingMethodMap, method) + delete(existingScopeMap, method) } // Remove old permissions - for method := range existingMethodMap { - if err := tx.Where("app_id = ? AND request_method = ?", userApp.ID, method).Delete(&db.AppPermission{}).Error; err != nil { + for method := range existingScopeMap { + if err := tx.Where("app_id = ? AND scope = ?", userApp.ID, method).Delete(&db.AppPermission{}).Error; err != nil { return err } } @@ -196,11 +179,11 @@ func (api *api) GetApp(userApp *db.App) *App { requestMethods := []string{} for _, appPerm := range appPermissions { expiresAt = appPerm.ExpiresAt - if appPerm.RequestMethod == nip47.PAY_INVOICE_METHOD { + if appPerm.Scope == nip47.PAY_INVOICE_METHOD { //find the pay_invoice-specific permissions paySpecificPermission = appPerm } - requestMethods = append(requestMethods, appPerm.RequestMethod) + requestMethods = append(requestMethods, appPerm.Scope) } //renewsIn := "" @@ -236,11 +219,11 @@ func (api *api) ListApps() ([]App, error) { dbApps := []db.App{} api.db.Find(&dbApps) - permissions := []db.AppPermission{} - api.db.Find(&permissions) + appPermissions := []db.AppPermission{} + api.db.Find(&appPermissions) permissionsMap := make(map[uint][]db.AppPermission) - for _, perm := range permissions { + for _, perm := range appPermissions { permissionsMap[perm.AppId] = append(permissionsMap[perm.AppId], perm) } @@ -255,14 +238,14 @@ func (api *api) ListApps() ([]App, error) { NostrPubkey: userApp.NostrPubkey, } - for _, permission := range permissionsMap[userApp.ID] { - apiApp.RequestMethods = append(apiApp.RequestMethods, permission.RequestMethod) - apiApp.ExpiresAt = permission.ExpiresAt - if permission.RequestMethod == nip47.PAY_INVOICE_METHOD { - apiApp.BudgetRenewal = permission.BudgetRenewal - apiApp.MaxAmount = uint64(permission.MaxAmount) + for _, appPermission := range permissionsMap[userApp.ID] { + apiApp.RequestMethods = append(apiApp.RequestMethods, appPermission.Scope) + apiApp.ExpiresAt = appPermission.ExpiresAt + if appPermission.Scope == permissions.PAY_INVOICE_SCOPE { + apiApp.BudgetRenewal = appPermission.BudgetRenewal + apiApp.MaxAmount = uint64(appPermission.MaxAmount) if apiApp.MaxAmount > 0 { - apiApp.BudgetUsage = api.permissionsSvc.GetBudgetUsage(&permission) + apiApp.BudgetUsage = api.permissionsSvc.GetBudgetUsage(&appPermission) } } } @@ -701,18 +684,17 @@ func (api *api) GetWalletCapabilities(ctx context.Context) (*WalletCapabilitiesR methods := strings.Split(api.svc.GetLNClient().GetSupportedNIP47Methods(), " ") notificationTypes := strings.Split(api.svc.GetLNClient().GetSupportedNIP47NotificationTypes(), " ") - // FIXME: permissions need to be decoupled from NIP-47 methods and notifications - permissions := utils.Filter(methods, func(s string) bool { + scopes := utils.Filter(methods, func(s string) bool { return s != nip47.PAY_KEYSEND_METHOD && s != nip47.MULTI_PAY_INVOICE_METHOD && s != nip47.MULTI_PAY_KEYSEND_METHOD }) if len(notificationTypes) > 0 { - permissions = append(permissions, "notifications") + scopes = append(scopes, "notifications") } return &WalletCapabilitiesResponse{ Methods: methods, NotificationTypes: notificationTypes, - Scopes: permissions, + Scopes: scopes, }, nil } diff --git a/api/models.go b/api/models.go index 1cc34fe7..5d27fd62 100644 --- a/api/models.go +++ b/api/models.go @@ -76,21 +76,20 @@ type ListAppsResponse struct { } type UpdateAppRequest struct { - MaxAmount uint64 `json:"maxAmount"` - BudgetRenewal string `json:"budgetRenewal"` - ExpiresAt string `json:"expiresAt"` - RequestMethods string `json:"requestMethods"` + MaxAmount uint64 `json:"maxAmount"` + BudgetRenewal string `json:"budgetRenewal"` + ExpiresAt string `json:"expiresAt"` + Scopes []string `json:"scopes"` } type CreateAppRequest struct { - Name string `json:"name"` - Pubkey string `json:"pubkey"` - MaxAmount uint64 `json:"maxAmount"` - BudgetRenewal string `json:"budgetRenewal"` - ExpiresAt string `json:"expiresAt"` - RequestMethods []string `json:"requestMethods"` - NotificationTypes []string `json:"notificationTypes"` - ReturnTo string `json:"returnTo"` + Name string `json:"name"` + Pubkey string `json:"pubkey"` + MaxAmount uint64 `json:"maxAmount"` + BudgetRenewal string `json:"budgetRenewal"` + ExpiresAt string `json:"expiresAt"` + Scopes []string `json:"scopes"` + ReturnTo string `json:"returnTo"` } type StartRequest struct { diff --git a/db/db_service.go b/db/db_service.go index 136895dc..85380ba2 100644 --- a/db/db_service.go +++ b/db/db_service.go @@ -20,7 +20,7 @@ func NewDBService(db *gorm.DB) *dbService { } } -func (svc *dbService) CreateApp(name string, pubkey string, maxAmount uint64, budgetRenewal string, expiresAt *time.Time, requestMethods []string) (*App, string, error) { +func (svc *dbService) CreateApp(name string, pubkey string, maxAmount uint64, budgetRenewal string, expiresAt *time.Time, scopes []string) (*App, string, error) { var pairingPublicKey string var pairingSecretKey string if pubkey == "" { @@ -44,11 +44,11 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmount uint64, bu return err } - for _, m := range requestMethods { + for _, scope := range scopes { appPermission := AppPermission{ - App: app, - RequestMethod: m, - ExpiresAt: expiresAt, + App: app, + Scope: scope, + ExpiresAt: expiresAt, //these fields are only relevant for pay_invoice MaxAmount: int(maxAmount), BudgetRenewal: budgetRenewal, @@ -58,6 +58,7 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmount uint64, bu return err } } + // commit transaction return nil }) diff --git a/db/migrations/202406301207_rename_request_methods.go b/db/migrations/202406301207_rename_request_methods.go new file mode 100644 index 00000000..3ccab1f8 --- /dev/null +++ b/db/migrations/202406301207_rename_request_methods.go @@ -0,0 +1,22 @@ +package migrations + +import ( + _ "embed" + + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +var _202406301207_rename_request_methods = &gormigrate.Migration{ + ID: "202406301207_rename_request_methods", + Migrate: func(tx *gorm.DB) error { + if err := tx.Exec("ALTER TABLE app_permissions RENAME request_method TO scope").Error; err != nil { + return err + } + + return nil + }, + Rollback: func(tx *gorm.DB) error { + return nil + }, +} diff --git a/db/migrations/migrate.go b/db/migrations/migrate.go index cdd3f6b9..b8c46447 100644 --- a/db/migrations/migrate.go +++ b/db/migrations/migrate.go @@ -14,6 +14,7 @@ func Migrate(gormDB *gorm.DB) error { _202405302121_store_decrypted_request, _202406061259_delete_content, _202406071726_vacuum, + _202406301207_rename_request_methods, }) return m.Migrate() diff --git a/db/models.go b/db/models.go index 23a75e86..541c7226 100644 --- a/db/models.go +++ b/db/models.go @@ -24,7 +24,7 @@ type AppPermission struct { ID uint AppId uint `validate:"required"` App App - RequestMethod string `validate:"required"` + Scope string `validate:"required"` MaxAmount int BudgetRenewal string ExpiresAt *time.Time @@ -68,7 +68,7 @@ type Payment struct { } type DBService interface { - CreateApp(name string, pubkey string, maxAmount uint64, budgetRenewal string, expiresAt *time.Time, requestMethods []string) (*App, string, error) + CreateApp(name string, pubkey string, maxAmount uint64, budgetRenewal string, expiresAt *time.Time, scopes []string) (*App, string, error) } const ( diff --git a/lnclient/models.go b/lnclient/models.go index 7970a7d4..067eb142 100644 --- a/lnclient/models.go +++ b/lnclient/models.go @@ -71,6 +71,7 @@ type LNClient interface { GetStorageDir() (string, error) GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error) UpdateLastWalletSyncRequest() + // TODO: change the below to return arrays instead of space-separated strings GetSupportedNIP47Methods() string GetSupportedNIP47NotificationTypes() string } diff --git a/nip47/controllers/get_info_controller.go b/nip47/controllers/get_info_controller.go index 23bd2ac1..4191c8c7 100644 --- a/nip47/controllers/get_info_controller.go +++ b/nip47/controllers/get_info_controller.go @@ -48,7 +48,7 @@ func (controller *getInfoController) HandleGetInfoEvent(ctx context.Context, nip } // basic permissions check - hasPermission, _, _ := controller.permissionsService.HasPermission(app, nip47Request.Method, 0) + hasPermission, _, _ := controller.permissionsService.HasPermission(app, permissions.GET_INFO_SCOPE, 0) if hasPermission { logger.Logger.WithFields(logrus.Fields{ "request_event_id": requestEventId, diff --git a/nip47/controllers/get_info_controller_test.go b/nip47/controllers/get_info_controller_test.go index 26aba407..f9af4e09 100644 --- a/nip47/controllers/get_info_controller_test.go +++ b/nip47/controllers/get_info_controller_test.go @@ -39,9 +39,9 @@ func TestHandleGetInfoEvent_NoPermission(t *testing.T) { assert.NoError(t, err) appPermission := &db.AppPermission{ - AppId: app.ID, - RequestMethod: models.GET_BALANCE_METHOD, - ExpiresAt: nil, + AppId: app.ID, + Scope: models.GET_BALANCE_METHOD, + ExpiresAt: nil, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) @@ -96,9 +96,9 @@ func TestHandleGetInfoEvent_WithPermission(t *testing.T) { assert.NoError(t, err) appPermission := &db.AppPermission{ - AppId: app.ID, - RequestMethod: models.GET_INFO_METHOD, - ExpiresAt: nil, + AppId: app.ID, + Scope: models.GET_INFO_METHOD, + ExpiresAt: nil, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) @@ -148,18 +148,18 @@ func TestHandleGetInfoEvent_WithNotifications(t *testing.T) { assert.NoError(t, err) appPermission := &db.AppPermission{ - AppId: app.ID, - RequestMethod: models.GET_INFO_METHOD, - ExpiresAt: nil, + AppId: app.ID, + Scope: models.GET_INFO_METHOD, + ExpiresAt: nil, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) // TODO: AppPermission RequestMethod needs to change to scope appPermission = &db.AppPermission{ - AppId: app.ID, - RequestMethod: "notifications", - ExpiresAt: nil, + AppId: app.ID, + Scope: "notifications", + ExpiresAt: nil, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) diff --git a/nip47/event_handler.go b/nip47/event_handler.go index 40545011..e606a655 100644 --- a/nip47/event_handler.go +++ b/nip47/event_handler.go @@ -13,6 +13,7 @@ import ( "github.com/getAlby/nostr-wallet-connect/logger" controllers "github.com/getAlby/nostr-wallet-connect/nip47/controllers" "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/nostr-wallet-connect/nip47/permissions" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/sirupsen/logrus" @@ -240,7 +241,17 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, sub *nostr.Subscriptio } checkPermission := func(amountMsat uint64) *models.Response { - hasPermission, code, message := svc.permissionsService.HasPermission(&app, nip47Request.Method, amountMsat) + scope, err := permissions.RequestMethodToScope(nip47Request.Method) + if err != nil { + return &models.Response{ + ResultType: nip47Request.Method, + Error: &models.Error{ + Code: models.ERROR_INTERNAL, + Message: err.Error(), + }, + } + } + hasPermission, code, message := svc.permissionsService.HasPermission(&app, scope, amountMsat) if !hasPermission { logger.Logger.WithFields(logrus.Fields{ "request_event_id": requestEvent.ID, diff --git a/nip47/models/models.go b/nip47/models/models.go index cd63d3ae..c74d49e5 100644 --- a/nip47/models/models.go +++ b/nip47/models/models.go @@ -7,20 +7,23 @@ import ( ) const ( - INFO_EVENT_KIND = 13194 - REQUEST_KIND = 23194 - RESPONSE_KIND = 23195 - NOTIFICATION_KIND = 23196 - PAY_INVOICE_METHOD = "pay_invoice" - GET_BALANCE_METHOD = "get_balance" - GET_INFO_METHOD = "get_info" - MAKE_INVOICE_METHOD = "make_invoice" - LOOKUP_INVOICE_METHOD = "lookup_invoice" - LIST_TRANSACTIONS_METHOD = "list_transactions" - PAY_KEYSEND_METHOD = "pay_keysend" - MULTI_PAY_INVOICE_METHOD = "multi_pay_invoice" - MULTI_PAY_KEYSEND_METHOD = "multi_pay_keysend" - SIGN_MESSAGE_METHOD = "sign_message" + INFO_EVENT_KIND = 13194 + REQUEST_KIND = 23194 + RESPONSE_KIND = 23195 + NOTIFICATION_KIND = 23196 + + // request methods + PAY_INVOICE_METHOD = "pay_invoice" + GET_BALANCE_METHOD = "get_balance" + GET_INFO_METHOD = "get_info" + MAKE_INVOICE_METHOD = "make_invoice" + LOOKUP_INVOICE_METHOD = "lookup_invoice" + LIST_TRANSACTIONS_METHOD = "list_transactions" + PAY_KEYSEND_METHOD = "pay_keysend" + MULTI_PAY_INVOICE_METHOD = "multi_pay_invoice" + MULTI_PAY_KEYSEND_METHOD = "multi_pay_keysend" + SIGN_MESSAGE_METHOD = "sign_message" + ERROR_INTERNAL = "INTERNAL" ERROR_NOT_IMPLEMENTED = "NOT_IMPLEMENTED" ERROR_QUOTA_EXCEEDED = "QUOTA_EXCEEDED" diff --git a/nip47/notifications/nip47_notifier.go b/nip47/notifications/nip47_notifier.go index a4cc492f..49b36b8a 100644 --- a/nip47/notifications/nip47_notifier.go +++ b/nip47/notifications/nip47_notifier.go @@ -104,7 +104,7 @@ func (notifier *Nip47Notifier) notifySubscribers(ctx context.Context, notificati notifier.db.Find(&apps) for _, app := range apps { - hasPermission, _, _ := notifier.permissionsSvc.HasPermission(&app, permissions.NOTIFICATIONS_PERMISSION, 0) + hasPermission, _, _ := notifier.permissionsSvc.HasPermission(&app, permissions.NOTIFICATIONS_SCOPE, 0) if !hasPermission { continue } diff --git a/nip47/notifications/nip47_notifier_test.go b/nip47/notifications/nip47_notifier_test.go index 64e3d6cb..08bc9e1b 100644 --- a/nip47/notifications/nip47_notifier_test.go +++ b/nip47/notifications/nip47_notifier_test.go @@ -25,9 +25,9 @@ func TestSendNotification_PaymentReceived(t *testing.T) { assert.NoError(t, err) appPermission := &db.AppPermission{ - AppId: app.ID, - App: *app, - RequestMethod: permissions.NOTIFICATIONS_PERMISSION, + AppId: app.ID, + App: *app, + Scope: permissions.NOTIFICATIONS_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) @@ -89,9 +89,9 @@ func TestSendNotification_PaymentSent(t *testing.T) { assert.NoError(t, err) appPermission := &db.AppPermission{ - AppId: app.ID, - App: *app, - RequestMethod: permissions.NOTIFICATIONS_PERMISSION, + AppId: app.ID, + App: *app, + Scope: permissions.NOTIFICATIONS_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) diff --git a/nip47/permissions/permissions.go b/nip47/permissions/permissions.go index 783c4776..b2e23b12 100644 --- a/nip47/permissions/permissions.go +++ b/nip47/permissions/permissions.go @@ -16,9 +16,15 @@ import ( "gorm.io/gorm" ) -// TODO: move other permissions here (e.g. all payment methods use pay_invoice) const ( - NOTIFICATIONS_PERMISSION = "notifications" + PAY_INVOICE_SCOPE = "pay_invoice" // also covers pay_keysend and multi_* payment methods + GET_BALANCE_SCOPE = "get_balance" + GET_INFO_SCOPE = "get_info" + MAKE_INVOICE_SCOPE = "make_invoice" + LOOKUP_INVOICE_SCOPE = "lookup_invoice" + LIST_TRANSACTIONS_SCOPE = "list_transactions" + SIGN_MESSAGE_SCOPE = "sign_message" + NOTIFICATIONS_SCOPE = "notifications" // covers all notification types ) type permissionsService struct { @@ -41,34 +47,30 @@ func NewPermissionsService(db *gorm.DB, eventPublisher events.EventPublisher) *p } } -func (svc *permissionsService) HasPermission(app *db.App, requestMethod string, amountMsat uint64) (result bool, code string, message string) { - switch requestMethod { - case models.PAY_INVOICE_METHOD, models.PAY_KEYSEND_METHOD, models.MULTI_PAY_INVOICE_METHOD, models.MULTI_PAY_KEYSEND_METHOD: - requestMethod = models.PAY_INVOICE_METHOD - } +func (svc *permissionsService) HasPermission(app *db.App, scope string, amountMsat uint64) (result bool, code string, message string) { appPermission := db.AppPermission{} findPermissionResult := svc.db.Find(&appPermission, &db.AppPermission{ - AppId: app.ID, - RequestMethod: requestMethod, + AppId: app.ID, + Scope: scope, }) if findPermissionResult.RowsAffected == 0 { // No permission for this request method - return false, models.ERROR_RESTRICTED, fmt.Sprintf("This app does not have permission to request %s", requestMethod) + return false, models.ERROR_RESTRICTED, fmt.Sprintf("This app does not have permission to request %s", scope) } expiresAt := appPermission.ExpiresAt if expiresAt != nil && expiresAt.Before(time.Now()) { logger.Logger.WithFields(logrus.Fields{ - "requestMethod": requestMethod, - "expiresAt": expiresAt.Unix(), - "appId": app.ID, - "pubkey": app.NostrPubkey, + "scope": scope, + "expiresAt": expiresAt.Unix(), + "appId": app.ID, + "pubkey": app.NostrPubkey, }).Info("This pubkey is expired") return false, models.ERROR_EXPIRED, "This app has expired" } - if requestMethod == models.PAY_INVOICE_METHOD { + if scope == PAY_INVOICE_SCOPE { maxAmount := appPermission.MaxAmount if maxAmount != 0 { budgetUsage := svc.GetBudgetUsage(&appPermission) @@ -92,18 +94,14 @@ func (svc *permissionsService) GetBudgetUsage(appPermission *db.AppPermission) u func (svc *permissionsService) GetPermittedMethods(app *db.App, lnClient lnclient.LNClient) []string { appPermissions := []db.AppPermission{} - // TODO: request_method needs to be renamed to scopes or capabilities - // see https://github.com/getAlby/nostr-wallet-connect-next/issues/219 - svc.db.Where("app_id = ? and request_method <> ?", app.ID, "notifications").Find(&appPermissions) - requestMethods := make([]string, 0, len(appPermissions)) + svc.db.Where("app_id = ?", app.ID).Find(&appPermissions) + scopes := make([]string, 0, len(appPermissions)) for _, appPermission := range appPermissions { - requestMethods = append(requestMethods, appPermission.RequestMethod) - } - if slices.Contains(requestMethods, models.PAY_INVOICE_METHOD) { - // all payment methods are tied to the pay_invoice permission - requestMethods = append(requestMethods, models.PAY_KEYSEND_METHOD, models.MULTI_PAY_INVOICE_METHOD, models.MULTI_PAY_KEYSEND_METHOD) + scopes = append(scopes, appPermission.Scope) } + requestMethods := scopesToRequestMethods(scopes) + // only return methods supported by the lnClient lnClientSupportedMethods := strings.Split(lnClient.GetSupportedNIP47Methods(), " ") requestMethods = utils.Filter(requestMethods, func(requestMethod string) bool { @@ -116,8 +114,8 @@ func (svc *permissionsService) GetPermittedMethods(app *db.App, lnClient lnclien func (svc *permissionsService) PermitsNotifications(app *db.App) bool { notificationPermission := db.AppPermission{} err := svc.db.First(¬ificationPermission, &db.AppPermission{ - AppId: app.ID, - RequestMethod: "notifications", + AppId: app.ID, + Scope: "notifications", }).Error if err != nil { return false @@ -149,3 +147,54 @@ func getStartOfBudget(budget_type string) time.Time { return time.Time{} } } + +func scopesToRequestMethods(scopes []string) []string { + requestMethods := []string{} + + for _, scope := range scopes { + scopeRequestMethods := scopeToRequestMethods(scope) + requestMethods = append(requestMethods, scopeRequestMethods...) + } + return requestMethods +} + +func scopeToRequestMethods(scope string) []string { + switch scope { + case PAY_INVOICE_SCOPE: + return []string{models.PAY_INVOICE_METHOD, models.PAY_KEYSEND_METHOD, models.MULTI_PAY_INVOICE_METHOD, models.MULTI_PAY_KEYSEND_METHOD} + case GET_BALANCE_SCOPE: + return []string{models.GET_BALANCE_METHOD} + case GET_INFO_SCOPE: + return []string{models.GET_INFO_METHOD} + case MAKE_INVOICE_SCOPE: + return []string{models.MAKE_INVOICE_METHOD} + case LOOKUP_INVOICE_SCOPE: + return []string{models.LOOKUP_INVOICE_METHOD} + case LIST_TRANSACTIONS_SCOPE: + return []string{models.LIST_TRANSACTIONS_METHOD} + case SIGN_MESSAGE_SCOPE: + return []string{models.SIGN_MESSAGE_METHOD} + } + return []string{} +} + +func RequestMethodToScope(requestMethod string) (string, error) { + switch requestMethod { + case models.PAY_INVOICE_METHOD, models.PAY_KEYSEND_METHOD, models.MULTI_PAY_INVOICE_METHOD, models.MULTI_PAY_KEYSEND_METHOD: + return PAY_INVOICE_SCOPE, nil + case models.GET_BALANCE_METHOD: + return GET_BALANCE_SCOPE, nil + case models.GET_INFO_METHOD: + return GET_INFO_SCOPE, nil + case models.MAKE_INVOICE_METHOD: + return MAKE_INVOICE_SCOPE, nil + case models.LOOKUP_INVOICE_METHOD: + return LOOKUP_INVOICE_SCOPE, nil + case models.LIST_TRANSACTIONS_METHOD: + return LIST_TRANSACTIONS_SCOPE, nil + case models.SIGN_MESSAGE_METHOD: + return SIGN_MESSAGE_SCOPE, nil + } + logger.Logger.WithField("request_method", requestMethod).Error("Unsupported request method") + return "", fmt.Errorf("unsupported request method: %s", requestMethod) +} diff --git a/nip47/permissions/permissions_test.go b/nip47/permissions/permissions_test.go index 9e7f19fb..2af4d509 100644 --- a/nip47/permissions/permissions_test.go +++ b/nip47/permissions/permissions_test.go @@ -19,7 +19,7 @@ func TestHasPermission_NoPermission(t *testing.T) { assert.NoError(t, err) permissionsSvc := NewPermissionsService(svc.DB, svc.EventPublisher) - result, code, message := permissionsSvc.HasPermission(app, models.PAY_INVOICE_METHOD, 100) + result, code, message := permissionsSvc.HasPermission(app, PAY_INVOICE_SCOPE, 100) assert.False(t, result) assert.Equal(t, models.ERROR_RESTRICTED, code) assert.Equal(t, "This app does not have permission to request pay_invoice", message) @@ -38,7 +38,7 @@ func TestHasPermission_Expired(t *testing.T) { appPermission := &db.AppPermission{ AppId: app.ID, App: *app, - RequestMethod: models.PAY_INVOICE_METHOD, + Scope: PAY_INVOICE_SCOPE, MaxAmount: 100, BudgetRenewal: budgetRenewal, ExpiresAt: &expiresAt, @@ -47,7 +47,7 @@ func TestHasPermission_Expired(t *testing.T) { assert.NoError(t, err) permissionsSvc := NewPermissionsService(svc.DB, svc.EventPublisher) - result, code, message := permissionsSvc.HasPermission(app, models.PAY_INVOICE_METHOD, 100) + result, code, message := permissionsSvc.HasPermission(app, PAY_INVOICE_SCOPE, 100) assert.False(t, result) assert.Equal(t, models.ERROR_EXPIRED, code) assert.Equal(t, "This app has expired", message) @@ -66,7 +66,7 @@ func TestHasPermission_Exceeded(t *testing.T) { appPermission := &db.AppPermission{ AppId: app.ID, App: *app, - RequestMethod: models.PAY_INVOICE_METHOD, + Scope: models.PAY_INVOICE_METHOD, MaxAmount: 10, BudgetRenewal: budgetRenewal, ExpiresAt: &expiresAt, @@ -75,7 +75,7 @@ func TestHasPermission_Exceeded(t *testing.T) { assert.NoError(t, err) permissionsSvc := NewPermissionsService(svc.DB, svc.EventPublisher) - result, code, message := permissionsSvc.HasPermission(app, models.PAY_INVOICE_METHOD, 100*1000) + result, code, message := permissionsSvc.HasPermission(app, PAY_INVOICE_SCOPE, 100*1000) assert.False(t, result) assert.Equal(t, models.ERROR_QUOTA_EXCEEDED, code) assert.Equal(t, "Insufficient budget remaining to make payment", message) @@ -94,7 +94,7 @@ func TestHasPermission_OK(t *testing.T) { appPermission := &db.AppPermission{ AppId: app.ID, App: *app, - RequestMethod: models.PAY_INVOICE_METHOD, + Scope: models.PAY_INVOICE_METHOD, MaxAmount: 10, BudgetRenewal: budgetRenewal, ExpiresAt: &expiresAt,