Files
sonr/x/svc/keeper/query_server.go
Prad NukalaandGitHub 13e6c3e84d Master (#1262)
* clear

* feat: Add everything

* fix: Commenht
2025-10-03 14:45:52 -04:00

309 lines
9.4 KiB
Go
Executable File

package keeper
import (
"context"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
apiv1 "github.com/sonr-io/sonr/api/svc/v1"
"github.com/sonr-io/sonr/x/svc/types"
)
// convertV1ServiceToTypes converts a v1.Service to types.Service
func convertV1ServiceToTypes(v1Service *apiv1.Service) *types.Service {
return &types.Service{
Id: v1Service.Id,
Domain: v1Service.Domain,
Owner: v1Service.Owner,
RootCapabilityCid: v1Service.RootCapabilityCid,
Permissions: v1Service.Permissions,
Status: types.ServiceStatus(v1Service.Status),
CreatedAt: v1Service.CreatedAt,
UpdatedAt: v1Service.UpdatedAt,
}
}
var _ types.QueryServer = Querier{}
type Querier struct {
Keeper
}
func NewQuerier(keeper Keeper) Querier {
return Querier{Keeper: keeper}
}
func (k Querier) Params(
c context.Context,
req *types.QueryParamsRequest,
) (*types.QueryParamsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
p, err := k.Keeper.Params.Get(ctx)
if err != nil {
return nil, err
}
return &types.QueryParamsResponse{Params: &p}, nil
}
// DomainVerification implements types.QueryServer.
func (k Querier) DomainVerification(
goCtx context.Context,
req *types.QueryDomainVerificationRequest,
) (*types.QueryDomainVerificationResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if req.Domain == "" {
return nil, fmt.Errorf("domain cannot be empty")
}
// Get domain verification from ORM
verification, err := k.Keeper.OrmDB.DomainVerificationTable().Get(ctx, req.Domain)
if err != nil {
return nil, fmt.Errorf("domain verification not found: %w", err)
}
// Convert v1.DomainVerification to types.DomainVerification
typesVerification := &types.DomainVerification{
Domain: verification.Domain,
Owner: verification.Owner,
VerificationToken: verification.VerificationToken,
Status: types.DomainVerificationStatus(verification.Status),
ExpiresAt: verification.ExpiresAt,
}
return &types.QueryDomainVerificationResponse{
DomainVerification: typesVerification,
}, nil
}
// Service implements types.QueryServer.
func (k Querier) Service(
goCtx context.Context,
req *types.QueryServiceRequest,
) (*types.QueryServiceResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if req.ServiceId == "" {
return nil, fmt.Errorf("service_id cannot be empty")
}
// Get service from ORM
service, err := k.Keeper.OrmDB.ServiceTable().Get(ctx, req.ServiceId)
if err != nil {
return nil, fmt.Errorf("service not found: %w", err)
}
return &types.QueryServiceResponse{
Service: convertV1ServiceToTypes(service),
}, nil
}
// ServicesByOwner implements types.QueryServer.
func (k Querier) ServicesByOwner(
goCtx context.Context,
req *types.QueryServicesByOwnerRequest,
) (*types.QueryServicesByOwnerResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if req.Owner == "" {
return nil, fmt.Errorf("owner cannot be empty")
}
// Create index key for owner
ownerKey := apiv1.ServiceOwnerIndexKey{}.WithOwner(req.Owner)
// List services by owner
iter, err := k.Keeper.OrmDB.ServiceTable().List(ctx, ownerKey)
if err != nil {
return nil, fmt.Errorf("failed to list services by owner: %w", err)
}
defer iter.Close()
var services []*types.Service
for iter.Next() {
service, err := iter.Value()
if err != nil {
return nil, fmt.Errorf("failed to get service value: %w", err)
}
services = append(services, convertV1ServiceToTypes(service))
}
return &types.QueryServicesByOwnerResponse{
Services: services,
}, nil
}
// ServicesByDomain implements types.QueryServer.
func (k Querier) ServicesByDomain(
goCtx context.Context,
req *types.QueryServicesByDomainRequest,
) (*types.QueryServicesByDomainResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if req.Domain == "" {
return nil, fmt.Errorf("domain cannot be empty")
}
// Create index key for domain
domainKey := apiv1.ServiceDomainIndexKey{}.WithDomain(req.Domain)
// List services by domain
iter, err := k.Keeper.OrmDB.ServiceTable().List(ctx, domainKey)
if err != nil {
return nil, fmt.Errorf("failed to list services by domain: %w", err)
}
defer iter.Close()
var services []*types.Service
for iter.Next() {
service, err := iter.Value()
if err != nil {
return nil, fmt.Errorf("failed to get service value: %w", err)
}
services = append(services, convertV1ServiceToTypes(service))
}
return &types.QueryServicesByDomainResponse{
Services: services,
}, nil
}
// ServiceOIDCDiscovery implements types.QueryServer.
func (k Querier) ServiceOIDCDiscovery(goCtx context.Context, req *types.QueryServiceOIDCDiscoveryRequest) (*types.QueryServiceOIDCDiscoveryResponse, error) {
if req == nil || req.ServiceId == "" {
return nil, types.ErrInvalidServiceID
}
// Get service to verify it exists and is active
service, err := k.Keeper.OrmDB.ServiceTable().Get(goCtx, req.ServiceId)
if err != nil {
return nil, types.ErrServiceNotFound
}
if service.Status != apiv1.ServiceStatus_SERVICE_STATUS_ACTIVE {
return nil, types.ErrServiceNotActive
}
// Get OIDC config
config, err := k.Keeper.GetServiceOIDCConfig(goCtx, req.ServiceId)
if err != nil {
// If no config exists, create default based on verified domain
config, err = k.Keeper.CreateDefaultOIDCConfig(goCtx, req.ServiceId, service.Domain)
if err != nil {
return nil, err
}
}
// Build OIDC discovery response according to spec
return &types.QueryServiceOIDCDiscoveryResponse{
Issuer: config.Issuer,
AuthorizationEndpoint: config.AuthorizationEndpoint,
TokenEndpoint: config.TokenEndpoint,
JwksUri: config.JwksUri,
UserinfoEndpoint: config.UserinfoEndpoint,
RegistrationEndpoint: fmt.Sprintf("https://%s/oauth/register", service.Domain),
ScopesSupported: config.ScopesSupported,
ResponseTypesSupported: config.ResponseTypesSupported,
GrantTypesSupported: config.GrantTypesSupported,
IdTokenSigningAlgValuesSupported: config.IdTokenSigningAlgValuesSupported,
SubjectTypesSupported: config.SubjectTypesSupported,
TokenEndpointAuthMethodsSupported: config.TokenEndpointAuthMethodsSupported,
ClaimsSupported: config.ClaimsSupported,
ResponseModesSupported: config.ResponseModesSupported,
ServiceDocumentation: fmt.Sprintf("https://%s/docs", service.Domain),
UiLocalesSupported: []string{"en-US"},
ClaimsLocalesSupported: []string{"en-US"},
RequestParameterSupported: true,
RequestUriParameterSupported: true,
RequireRequestUriRegistration: false,
OpPolicyUri: fmt.Sprintf("https://%s/policy", service.Domain),
OpTosUri: fmt.Sprintf("https://%s/terms", service.Domain),
}, nil
}
// ServiceOIDCJWKS implements types.QueryServer.
func (k Querier) ServiceOIDCJWKS(goCtx context.Context, req *types.QueryServiceOIDCJWKSRequest) (*types.QueryServiceOIDCJWKSResponse, error) {
if req == nil || req.ServiceId == "" {
return nil, types.ErrInvalidServiceID
}
// Get service to verify it exists
service, err := k.Keeper.OrmDB.ServiceTable().Get(goCtx, req.ServiceId)
if err != nil {
return nil, types.ErrServiceNotFound
}
if service.Status != apiv1.ServiceStatus_SERVICE_STATUS_ACTIVE {
return nil, types.ErrServiceNotActive
}
// Get JWKS
jwks, err := k.Keeper.GetServiceJWKS(goCtx, req.ServiceId)
if err != nil {
// If no JWKS exists, create default
jwks, err = k.Keeper.CreateDefaultJWKS(goCtx, req.ServiceId)
if err != nil {
return nil, err
}
}
// Return JWKS response
return &types.QueryServiceOIDCJWKSResponse{
Keys: jwks.Keys,
}, nil
}
// ServiceOIDCMetadata implements types.QueryServer.
func (k Querier) ServiceOIDCMetadata(goCtx context.Context, req *types.QueryServiceOIDCMetadataRequest) (*types.QueryServiceOIDCMetadataResponse, error) {
if req == nil || req.ServiceId == "" {
return nil, types.ErrInvalidServiceID
}
// Get service
service, err := k.Keeper.OrmDB.ServiceTable().Get(goCtx, req.ServiceId)
if err != nil {
return nil, types.ErrServiceNotFound
}
// Get OIDC config
config, err := k.Keeper.GetServiceOIDCConfig(goCtx, req.ServiceId)
if err != nil {
// If no config exists, create default based on verified domain
config, err = k.Keeper.CreateDefaultOIDCConfig(goCtx, req.ServiceId, service.Domain)
if err != nil {
return nil, err
}
}
// Convert service status
var serviceStatus types.ServiceStatus
switch service.Status {
case apiv1.ServiceStatus_SERVICE_STATUS_ACTIVE:
serviceStatus = types.ServiceStatus_SERVICE_STATUS_ACTIVE
case apiv1.ServiceStatus_SERVICE_STATUS_SUSPENDED:
serviceStatus = types.ServiceStatus_SERVICE_STATUS_SUSPENDED
case apiv1.ServiceStatus_SERVICE_STATUS_REVOKED:
serviceStatus = types.ServiceStatus_SERVICE_STATUS_REVOKED
default:
serviceStatus = types.ServiceStatus_SERVICE_STATUS_ACTIVE
}
// Build metadata response
return &types.QueryServiceOIDCMetadataResponse{
Config: config,
VerifiedDomain: service.Domain,
ServiceStatus: serviceStatus,
Metadata: map[string]string{
"service_id": service.Id,
"owner": service.Owner,
"created_at": fmt.Sprintf("%d", service.CreatedAt),
"updated_at": fmt.Sprintf("%d", service.UpdatedAt),
"ucan_root_cid": service.RootCapabilityCid,
"oidc_enabled": "true",
},
}, nil
}