@@ -70,10 +70,27 @@ def plaintext_secret
7070 end
7171 end
7272
73- def to_json ( options = nil )
74- serializable_hash ( except : :secret )
75- . merge ( secret : plaintext_secret )
76- . to_json ( options )
73+ # Represents client as set of it's attributes in JSON format.
74+ # This is the right way how we want to override ActiveRecord #to_json.
75+ #
76+ # Respects privacy settings and serializes minimum set of attributes
77+ # for public/private clients and full set for authorized owners.
78+ #
79+ # @return [Hash] entity attributes for JSON
80+ #
81+ def as_json ( options = { } )
82+ # if application belongs to some owner we need to check if it's the same as
83+ # the one passed in the options or check if we render the client as an owner
84+ if ( respond_to? ( :owner ) && owner && owner == options [ :current_resource_owner ] ) ||
85+ options [ :as_owner ]
86+ # Owners can see all the client attributes, fallback to ActiveModel serialization
87+ super
88+ else
89+ # if application has no owner or it's owner doesn't match one from the options
90+ # we render only minimum set of attributes that could be exposed to a public
91+ only = extract_serializable_attributes ( options )
92+ super ( options . merge ( only : only ) )
93+ end
7794 end
7895
7996 private
@@ -98,5 +115,48 @@ def scopes_match_configured
98115 def enforce_scopes?
99116 Doorkeeper . configuration . enforce_configured_scopes?
100117 end
118+
119+ # Helper method to extract collection of serializable attribute names
120+ # considering serialization options (like `only`, `except` and so on).
121+ #
122+ # @param options [Hash] serialization options
123+ #
124+ # @return [Array<String>]
125+ # collection of attributes to be serialized using #as_json
126+ #
127+ def extract_serializable_attributes ( options = { } )
128+ opts = options . try ( :dup ) || { }
129+ only = Array . wrap ( opts [ :only ] ) . map ( &:to_s )
130+
131+ only = if only . blank?
132+ serializable_attributes
133+ else
134+ only & serializable_attributes
135+ end
136+
137+ only -= Array . wrap ( opts [ :except ] ) . map ( &:to_s ) if opts . key? ( :except )
138+ only . uniq
139+ end
140+
141+ # We need to hook into this method to allow serializing plan-text secrets
142+ # when secrets hashing enabled.
143+ #
144+ # @param key [String] attribute name
145+ #
146+ def read_attribute_for_serialization ( key )
147+ return super unless key . to_s == "secret"
148+
149+ plaintext_secret || secret
150+ end
151+
152+ # Collection of attributes that could be serialized for public.
153+ # Override this method if you need additional attributes to be serialized.
154+ #
155+ # @return [Array<String>] collection of serializable attributes
156+ def serializable_attributes
157+ attributes = %w[ id name created_at ]
158+ attributes << "uid" unless confidential?
159+ attributes
160+ end
101161 end
102162end
0 commit comments