refactor: remove redundant dtos

This commit is contained in:
Elias Schneider
2026-07-07 11:51:37 +02:00
parent 2f55b7cbc3
commit fa2d08cb6d
6 changed files with 21 additions and 38 deletions

View File

@@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"github.com/pocket-id/pocket-id/backend/internal/dto"
"github.com/pocket-id/pocket-id/backend/internal/oidc"
)
@@ -39,19 +40,19 @@ func (m *Module) AllowedScopesForAudience(ctx context.Context, tx *gorm.DB, clie
}
// DescribePermissions implements the OIDC module's APIAccessProvider interface
func (m *Module) DescribePermissions(ctx context.Context, audience string, keys []string) ([]oidc.PermissionInfo, error) {
func (m *Module) DescribePermissions(ctx context.Context, audience string, keys []string) ([]dto.ScopeInfoDto, error) {
permissions, err := m.service.DescribePermissions(ctx, audience, keys)
if err != nil {
return nil, err
}
infos := make([]oidc.PermissionInfo, len(permissions))
infos := make([]dto.ScopeInfoDto, len(permissions))
for i, permission := range permissions {
description := ""
if permission.Description != nil {
description = *permission.Description
}
infos[i] = oidc.PermissionInfo{Key: permission.Key, Name: permission.Name, Description: description}
infos[i] = dto.ScopeInfoDto{Key: permission.Key, Name: permission.Name, Description: description}
}
return infos, nil

View File

@@ -5,6 +5,7 @@ import (
"slices"
"github.com/ory/fosite"
"github.com/pocket-id/pocket-id/backend/internal/dto"
"gorm.io/gorm"
)
@@ -24,13 +25,6 @@ func isStandardScope(scope string) bool {
return slices.Contains(standardScopes, scope)
}
// PermissionInfo is the display information for an API permission used to show friendly names and descriptions on the consent screen
type PermissionInfo struct {
Key string
Name string
Description string
}
// APIAccessProvider is implemented by the api feature module
// It lets the OIDC module widen per-client scope and audience validation and resolve RFC 8707 resources to the permission keys a client may be granted
type APIAccessProvider interface {
@@ -40,7 +34,7 @@ type APIAccessProvider interface {
AllowedScopesForAudience(ctx context.Context, tx *gorm.DB, clientID, audience string, subjectType SubjectType) (scopes []string, apiExists bool, err error)
// DescribePermissions returns the display information for the given permission keys of the API identified by audience
// Unknown keys are omitted
DescribePermissions(ctx context.Context, audience string, keys []string) ([]PermissionInfo, error)
DescribePermissions(ctx context.Context, audience string, keys []string) ([]dto.ScopeInfoDto, error)
}
// resolveResource maps an RFC 8707 resource, which may be empty, to the audience to stamp on the issued token and the subset of requestedScopes that may be granted

View File

@@ -8,6 +8,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"github.com/pocket-id/pocket-id/backend/internal/dto"
)
// fakeAPIAccess implements APIAccessProvider from an audience -> subject type -> allowed-scopes map.
@@ -50,16 +52,16 @@ func (f fakeAPIAccess) AllowedScopesForAudience(_ context.Context, _ *gorm.DB, _
return bySubject[subjectType], true, nil
}
func (f fakeAPIAccess) DescribePermissions(_ context.Context, audience string, keys []string) ([]PermissionInfo, error) {
func (f fakeAPIAccess) DescribePermissions(_ context.Context, audience string, keys []string) ([]dto.ScopeInfoDto, error) {
bySubject, ok := f.allowed[audience]
if !ok {
return nil, nil
}
var infos []PermissionInfo
var infos []dto.ScopeInfoDto
for _, key := range keys {
for _, allowed := range bySubject {
if slices.Contains(allowed, key) {
infos = append(infos, PermissionInfo{Key: key, Name: key})
infos = append(infos, dto.ScopeInfoDto{Key: key, Name: key})
break
}
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/ory/fosite"
"github.com/pocket-id/pocket-id/backend/internal/common"
"github.com/pocket-id/pocket-id/backend/internal/dto"
"github.com/pocket-id/pocket-id/backend/internal/model"
datatype "github.com/pocket-id/pocket-id/backend/internal/model/types"
"github.com/pocket-id/pocket-id/backend/internal/utils"
@@ -525,7 +526,7 @@ func (s *authorizationService) buildInteractionForUser(ctx context.Context, inte
}
// Always serialize a possibly empty array rather than null
if scopeInfo == nil {
scopeInfo = []scopeInfoDto{}
scopeInfo = []dto.ScopeInfoDto{}
}
result.ScopeInfo = scopeInfo
@@ -534,13 +535,13 @@ func (s *authorizationService) buildInteractionForUser(ctx context.Context, inte
// resolveScopeInfo resolves display names and descriptions for the requested non-standard scopes, looked up against the API targeted by the request's RFC 8707 resource
// Standard identity scopes are rendered by the client
func (s *authorizationService) resolveScopeInfo(ctx context.Context, interactionSession InteractionSession) ([]scopeInfoDto, error) {
func (s *authorizationService) resolveScopeInfo(ctx context.Context, interactionSession InteractionSession) ([]dto.ScopeInfoDto, error) {
return s.resolveScopeInfoForRequest(ctx, interactionSession.Parameters["resource"], interactionSession.Scopes)
}
// resolveScopeInfoForRequest resolves display names and descriptions for the requested non-standard scopes against the API identified by resource
// The browser and device consent flows share it so both show friendly permission names instead of raw scope keys
func (s *authorizationService) resolveScopeInfoForRequest(ctx context.Context, resource string, scopes []string) ([]scopeInfoDto, error) {
func (s *authorizationService) resolveScopeInfoForRequest(ctx context.Context, resource string, scopes []string) ([]dto.ScopeInfoDto, error) {
if s.apiAccess == nil {
return nil, nil
}
@@ -564,12 +565,7 @@ func (s *authorizationService) resolveScopeInfoForRequest(ctx context.Context, r
return nil, err
}
scopeInfo := make([]scopeInfoDto, len(infos))
for i, info := range infos {
scopeInfo[i] = scopeInfoDto(info)
}
return scopeInfo, nil
return infos, nil
}
func (s *authorizationService) completeInteractionStep(ctx context.Context, interactionSessionID, userID string, step interactionStep, reauthenticationToken string, authenticationTime time.Time, meta requestMeta) (completeInteractionResponse, error) {

View File

@@ -50,6 +50,7 @@ func (s *deviceService) createDeviceAuthorization(ctx context.Context, req *http
return nil, request, err
}
// Validate the requested scopes and resolve the resource indicator to an audience and the subset of requested scopes that may be granted
client := request.GetClient().(Client)
resource, err := request.GetResource()
if err != nil {
@@ -192,13 +193,8 @@ func (s *deviceService) getDeviceCodeInfo(ctx context.Context, userCode, userID
return nil, err
}
// Always serialize a possibly empty array rather than null
scopeInfoDtos := make([]dto.ScopeInfoDto, len(scopeInfo))
for i, info := range scopeInfo {
scopeInfoDtos[i] = dto.ScopeInfoDto{
Key: info.Key,
Name: info.Name,
Description: info.Description,
}
if scopeInfo == nil {
scopeInfo = []dto.ScopeInfoDto{}
}
return &dto.DeviceCodeInfoDto{
@@ -211,7 +207,7 @@ func (s *deviceService) getDeviceCodeInfo(ctx context.Context, userCode, userID
RequiresReauthentication: client.RequiresReauthentication,
},
Scope: request.GetRequestedScopes(),
ScopeInfo: scopeInfoDtos,
ScopeInfo: scopeInfo,
AuthorizationRequired: authorizationRequired,
ReauthenticationRequired: client.RequiresReauthentication,
}, nil

View File

@@ -16,18 +16,12 @@ const (
type interactionSessionForUser struct {
ID string `json:"id"`
Scopes []string `json:"scopes"`
ScopeInfo []scopeInfoDto `json:"scopeInfo"`
ScopeInfo []dto.ScopeInfoDto `json:"scopeInfo"`
Client dto.OidcClientMetaDataDto `json:"client"`
CurrentStep interactionStep `json:"currentStep,omitempty"`
RequiredSteps []interactionStep `json:"requiredSteps"`
}
type scopeInfoDto struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
type completeInteractionRequest struct {
Step interactionStep `json:"step"`
}