[HN Gopher] A Replacement for Strong Parameters
       ___________________________________________________________________
        
       A Replacement for Strong Parameters
        
       Author : todsacerdoti
       Score  : 33 points
       Date   : 2022-11-13 14:38 UTC (8 hours ago)
        
 (HTM) web link (ryanbigg.com)
 (TXT) w3m dump (ryanbigg.com)
        
       | Fire-Dragon-DoL wrote:
       | We already do something similar. Strong parameters is a terrible
       | api since it splits the schema and tge "authorized parameters",
       | which is really just the same.
       | 
       | Aside from that, I don't love dry libs either, they are often
       | very complex for their usecases and they all have DSLs that could
       | have been better served by using ruby, datastructures and objects
        
       | thwaway2423 wrote:
       | My personal opinion is that malformed data shouldn't be reaching
       | domain code.
       | 
       | A unique schema for each route that is strongly typed which can
       | be turned into a specialized streaming JSON parser.
       | 
       | Given Rails is heavily dynamic, it reaches lawlessness and with
       | no regards to data integrity, eventually leading me to decide to
       | exit ruby and rails altogether.
       | 
       | With Ruby (& Rails) every single parameter is suspect and cannot
       | be trusted. Stripe's type checker does alleviate some of this
       | stress and anxiety.
        
         | rubyist5eva wrote:
         | This is the main reason why I use graphql on my Ruby web
         | projects. The client flexibility is nice. Performance issues
         | from wonky queries sent from clients can be a pain to debug -
         | thankfully it's an internal API so I can work directly with the
         | client devs to create optimal queries. But the strong type
         | guaranties and schema validation is the main reason why I
         | prefer graphql over rails controllers with strong parameters.
        
           | sparker72678 wrote:
           | Do you find the type definition work to be better/easier than
           | using model validations? I've found that I prefer that logic
           | in the model instead of type def files, but hey, to each
           | their own, of course.
        
             | rubyist5eva wrote:
             | I use a mix of both. GraphQL is used to validate the schema
             | and types of the input, model validations are used to
             | ensure that the actual values being saved to the database
             | make sense from a domain perspective.
        
         | derefr wrote:
         | > My personal opinion is that malformed data shouldn't be
         | reaching domain code.
         | 
         | The mistake a lot of people make: Rails itself _isn 't domain
         | code_.
         | 
         | In hexagonal-architecture terms, Rails is a framework for
         | building your "web gateway", not for building your domain
         | logic. Your domain logic should live on its own, _outside_
         | Rails, as plain-old code with a well-defined API (consisting of
         | functions and domain types.)
         | 
         | Rails' job -- like any other hexagonal-architecture gateway's
         | job -- is to _translate in_ incoming requests into command or
         | query objects to pass to the domain-logic API; and to
         | _translate_ the domain-logic API 's generated events or query-
         | result objects back out. The former is exactly an MVC
         | controller; the latter is exactly an MVC view.
         | 
         | Think of it like building a (portable, multiplatform) game:
         | your business-logic is like the game engine, assets, scripting,
         | etc. Rails, meanwhile, is like the OS-specific glue code that
         | makes your game run in a window and respond to mouse/keyboard
         | input on Windows/macOS/Linux etc. This glue code takes events
         | from each OS and brings them into your game; takes output from
         | your game and feeds it back to OS drivers; and binds OS
         | mechanisms like window controls to game features. You don't
         | want any "game" logic to live inside your OS-specific glue
         | code. You want your glue code to just be a "wrapper" that
         | brings in your game as, essentially, a library, and then "wires
         | it up" to the given OS.
         | 
         | In this view, both Strong Parameters and dry-schema are tools
         | used "at parse time" -- i.e. controller-execution time within
         | the web gateway -- to create the "strongly typed" "unique
         | schema" for the route, that allows it to discard/reject data
         | before putting it into the real strongly-typed objects: the
         | business-domain objects used by your business-logic DDD
         | context.
         | 
         | (And the reason that this gluing-together is done through an
         | arbitrarily-programmable framework, rather than by just giving
         | routes static strong types, is because the translation process
         | itself -- especially when compatibility with multiple legacy
         | systems is involved -- can be "Turing-hard", _requiring_ a full
         | programming language to specify what should be glued to what,
         | and when it 's valid to translate X to Y vs X to Z. Maybe some
         | users are talking to v1.1 of an endpoint while some users are
         | talking to v1.0, based on a per-user feature-flag or holdback-
         | flag or whether the user is a paying customer or not.)
        
       | latortuga wrote:
       | One of my devs recently pitched an idea to me that I can't stop
       | thinking about.
       | 
       | Strong Params requires you to enumerate all params on the server.
       | But why couldn't you know what are valid params while building
       | your form? You could serialize the params shape in the form
       | builder and include it in your post payload, signed by the app
       | secret to avoid tampering.
       | 
       | It should have done this from the beginning!
        
         | derefr wrote:
         | You're talking about pure validation, though. dry-schema and
         | the like, though, are more like in-memory ETL libraries for
         | form data: they don't just describe a valid parameters shape,
         | but also what -- and how -- to extract from those parameters,
         | and an (implied) data-structure shape + typings to put the
         | results into.
         | 
         | I can see how you could generate the Extract step of an ETL
         | process (i.e. a validation specification) purely from the form
         | HTML itself. But I'm not sure where you'd stuff the information
         | required to do the Transform or Load steps. It isn't cleanly
         | separable into per-component data attributes.
        
         | jherdman wrote:
         | Because a well crafted response could write whatever they
         | wanted to your DB as you've given the client too much control.
         | 
         | If I know, for instance, an admin is identified by an
         | `is_admin` flag, I could add this to the allowed params and
         | send my flag. Bam. I'm now an admin.
         | 
         | I know the example is a bit simplistic, but it wouldn't take
         | long to comb your app for a real world example where this would
         | be problematic.
        
           | matthewmacleod wrote:
           | That's the point of the signature the above poster suggested
           | - if you use the app secret to generate a signature of the
           | valid parameters, then the client can't edit this list
           | without invalidating the signature and there is no risk of
           | them modifying other fields. If your secret is compromised
           | such that this becomes possible, you have way bigger
           | problems.
           | 
           | I'm not super keen myself as I prefer the implementation to
           | be much more explicit than magic, but there doesn't seem to
           | be an obvious security hole here.
        
             | Izkata wrote:
             | These attacks work by modifying values, not adding
             | parameters. Sounds like they would still work.
             | 
             | Besides, you still have to validate the signature server-
             | side, so it's not like it's saving any work. Validation
             | just gets split up between generating the signature, a
             | network round-trip, and validating the signature.
        
               | derefr wrote:
               | > so it's not like it's saving any work
               | 
               | The "work" the GP is trying to save here isn't CPU
               | cycles, but rather developer labor -- the redundant labor
               | of writing both a form view that describes form inputs,
               | and a form-value schema validator to be called from the
               | controller that receives the form's submitted input
               | values.
               | 
               | Generating the signature, and validating the signature,
               | would both be done transparently by middleware components
               | of the framework, with no marginal developer labor
               | required per form.
        
             | latortuga wrote:
             | Precisely.
             | 
             | Strong Params is a canonical violation of DRY. When you
             | build your form, you fill it with fields that are allowed
             | to be filled out and submitted. Then you have to duplicate
             | that work in the controller for no obvious benefit.
             | 
             | As for "more explicit than magic" I prefer to think of this
             | as "automatic" or "conventional" rather than magic. Much of
             | Rails' "magic" is this exact kind of thing, allowing the
             | right thing to automatically happen with the ability to
             | step in or override manually when the convention isn't what
             | you want.
             | 
             | I get why you'd want it to be more explicit. I think that
             | this feature was invented to solve a security problem and
             | the solution was to give devs more work to do. There's no
             | obvious reason why it couldn't be automatically taken care
             | of for the dev and it would definitely make maintenance and
             | legacy app upgrades smoother.
        
         | jeremyevans wrote:
         | There is a Ruby form library named Forme that works this way
         | for Roda and Sequel (https://forme.jeremyevans.net/files/README
         | _rdoc.html#label-R...). As you expect, this makes handling
         | normal HTML form submissions much easier.
        
           | claudiug wrote:
           | just want to say Roda and Sequel deserve a better marketing,
           | you need a dhh, as they are great!
        
         | ghiculescu wrote:
         | This wouldn't work for APIs or controllers that you make
         | requests to via JS, but for the default use case of forms it's
         | an _incredible_ idea.
         | 
         | It would probably be not too hard to do as a gem - you inject a
         | hidden field when form_with is called and you define a new
         | method on params so the controller can indicate that this
         | approach is being used.
        
         | pmontra wrote:
         | I'm not sure that I understood your suggestion. What if the the
         | client is curl -F name=Ryan -F age=34 ? How would I sign that
         | request and why?
        
           | mnutt wrote:
           | It would only work for routes that only accept rails form_for
           | input, and would reject requests without the signature.
        
       | kevinmchugh wrote:
       | I think strong_params is a pretty weak way to do parameter
       | parsing and share the author's frustration. I'm out of rails-land
       | now, and using openapi docs and generated middleware to handle
       | all that for me. We're able to give the docs to customers and
       | able to generate code for API clients.
       | 
       | dry_schema looks nice and a certain improvement over strong
       | params, but I'd go a step further.
        
       | davidw wrote:
       | To me, some of the 'dry' libraries are useful, but some feel a
       | bit like Haskell-envy. They provide some value, but also some
       | overhead and cognitive load, and in some cases just feel a bit
       | odd.
        
         | rubyist5eva wrote:
         | recently had a new hire try to add dry-monad to our monolith
         | and I had to explain to him that while I personally don't mind
         | it, having to explain monads and have other people that don't
         | necessarily understand monads maintain the code is not a good
         | idea and that there are perfectly idiomatic Ruby constructs
         | that any developper could maintain that can accomplish the same
         | thing
         | 
         | He doesn't work for us anymore (not because of this)
        
       | mnutt wrote:
       | I agree the syntax for strong_params is confusing and error
       | prone, but I wish the author had drawn more of a distinction
       | between the schema of the request payload versus the schema of
       | the model. Rails assumes the model should handle its own type
       | validations, and strong_params is sort of a bandaid to ensure the
       | params object can't affect internal implementation details.
       | dry_schema looks way more powerful but it makes it easier for one
       | to forget that the primary goal is not a generalized schema, but
       | a security boundary for ensuring dangerous attributes aren't set
       | on the model.
       | 
       | I think the pattern is helpful but it's still pretty easy to mess
       | up.
        
       | chucke wrote:
       | As bad as strong parameters is, it was still, at the time, a
       | decent improvement over its predecessor, attr_accessible.
        
       | sparker72678 wrote:
       | If you disagree with just about every Rails convention, just
       | don't use Rails. There are other options in the world.
       | 
       | Ryan is a smart guy, but it seems like he'd be a lot happier with
       | something that isn't Rails.
        
       ___________________________________________________________________
       (page generated 2022-11-13 23:02 UTC)