mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
309 lines
9.4 KiB
Go
Executable File
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
|
|
}
|