85202

How to prevent client from changing objectify @ID in Google Cloud Endpoints

Question:

I am using objectify to persist objects to Google Cloud datastore. The "primary key" is annotated with @Id.

@Entity class Car { @Id Long id; ... }

I generate the client endpoint (using Android Studio).

I need getters and setters for all fields, including id, so that REST is able to serialize and deserialize the object. If I don't add getters and setters, then these are created by the Google endpoint builder.

Normally, the client GETs an instance from datastore, changes attributes and UPDATEs it back. The backend processes the new attributes and stores them in datastore.

As long as the id is the same, this works as supposed. But what happens when the client changes the id? Because the server is stateless, it believes another object has been updated and either inserts or updates the wrong object. In other words, if the new id already exists in datastore, then that record is updated; if it doesn't exist then a new record with that id is created.

This behavior is inherent to datastore, but this will mess up the database if the client ever changes the id, so there must be a way to prevent this.

I can see two solutions:

<ol><li>

As suggested here <a href="https://stackoverflow.com/questions/23686429/gae-getter-without-setter-how-to-prevent-client-from-writing-a-given-propert" rel="nofollow">GAE - Getter without Setter - How to prevent client from writing a given property? How to prevent client from modifying the object ID?</a> by @zgc7009, I could leave the setter empty and let it do nothing. I have tried this and it works, as long as there are no related objects.

</li> <li>

I could use a version field on the backend, which I increment each time UPDATE is called. This allows me to verify if client version == server version - 1 and throw an exception if not. This measure would reduce chances of updating the wrong record, but it would require a read from the datastore before each write and therefore isn't efficient.

</li> </ol>

As both solutions are not nice, I believe somebody at Google has been thinking about this and there must be a better way to ensure data integrity across REST. Due to my understanding - and I don't know if that can be changed -, Google Endpoints API is generated in a way where the id is not included within the URL, or this happens under the cover. In other words: Normally the id under which an object is stored is included with the URL (metadata). Here, it is included with the object (payload) and can therefore be changed by the client.

Can anybody shed light please?

PS: I know that best practices for datastore in above example would probably using the VIN as id, but this is not always possible. There are objects where no unique identifier can be created for use as id.

Answer1:

Looks like a common problem many have faced. There is one thing you can do:

<em>Your solution 2 above is a good one but you're worried about reading from the Datastore before each write</em>. This can be mitigated by annotating your entity class with @Cache so that the first time a client retrieves entities they will be cached. This way when the client calls your backend to update or do whatever you will not hit the Datastore when comparing the versions.

Answer2:

If you are worried about exposing sensitive attributes over your endpoint you have 2 alternatives:

1) annotate them (getters) with @ApiResourceProperty(ignored=AnnotationBoolean.TRUE) so the endpoints serializer skips them.

2) Don't send your entities over the API, create basic POJOs with only the fields you want/need to transfer.

Recommend

  • IS there any application controller for .net mvc like ruby on rails?
  • Word Base/Stem Dictionary
  • Parsing Java String to date
  • jQuery.fn.load() is deprecated?
  • JAXB 2 in an Oracle 10g Webapp
  • How should I add a stationary progress bar to a C++ program that produces terminal output (in Linux)
  • Inno Setup Search for specifc file on a CD, retrieve exact filepath and return value to [Files]-Sect
  • Is there an easy way to associate an event with a ListViewItem?
  • help('modules') crashing? Not sure how to fix
  • Can XOR be expressed using SKI combinators?
  • Changing media screen makes div overlay
  • Compare struct to a constant in C
  • Consuming a WCF service in a Java Client using wsHttpBinding
  • Wrong labels when plotting a time series pandas dataframe with matplotlib
  • Bash if statement with multiple conditions
  • Updating both a ConcurrentHashMap and an AtomicInteger safely
  • Android application: how to use the camera and grab the image bytes?
  • Tamper-proof configuration files in .NET?
  • How to add git credentials to the build so it would be able to be used within a shell code?
  • How can the INSERT … ON CONFLICT (id) DO UPDATE… syntax be used with a sequence ID?
  • Marklogic : Query response time is very high
  • Groovy: Unexpected token “:”
  • Xcode 4 NSLog Macro link in Xcode 3
  • Why querying a date BC is changed to AD in Java?
  • Record samples being played with OpenAL
  • How to create a file in java without a extension
  • QLineEdit password safety
  • Resize panoramic image to fixed size
  • Volusion's generic SQL folder, functionality
  • How to extract text from Word files using C#?
  • How to check if every primary key value is being referenced as foreign key in another table
  • Sending data from AppleScript to FileMaker records
  • MySQL WHERE-condition in procedure ignored
  • PHP: When would you need the self:: keyword?
  • How do you join a server to an Active Directory (domain)?
  • coudnt use logback because of log4j
  • File not found error Google Drive API
  • Qt: Run a script BEFORE make
  • Does armcc optimizes non-volatile variables with -O0?
  • How to get NHibernate ISession to cache entity not retrieved by primary key