SimpleCouchbaseRepository.findById() and CouchbaseTemplate.findById() do not validate the document type
See original GitHub issueIt is possible to save() a document of one type, and retrieve it using another type.
The below example will succeed, where one might expect it to fail.
public class Group {
@Id
@GeneratedValue(strategy = GenerationStrategy.UNIQUE)
String id;
@Field
String name;
}
@Repository
public interface GroupRepository extends CouchbaseRepository<Group, String> {
}
public class User {
@Id
@GeneratedValue(strategy = GenerationStrategy.UNIQUE)
String id;
@Field
String name;
}
@Repository
public interface UserRepository extends CouchbaseRepository<User, String> {
}
@SpringBootApplication
@EnableCouchbaseRepositories
public class ExampleApplication implements ApplicationRunner {
@Autowired
private UserRepository userRepository;
@Autowired
private GroupRepository groupRepository;
public static void main(final String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
@Override
public void run(final ApplicationArguments args) throws Exception {
User user = userRepository.save(new User());
System.out.println(user.id);
System.out.println(groupRepository.findById(user.id)
.get().id);
}
}
The underlying code in SimpleCouchbaseRepository#findById
calls
return Optional.ofNullable(couchbaseOperations.findById(entityInformation.getJavaType()).one(id.toString()));
CouchbaseTemplate
should at least try and validate that the supplied type matches the retrieved document type, and either return null (my personal preference), or throw an exception that the underlying type does not match (still acceptable).
We had implemented a workaround for the previous Spring Data Couchbase 3.x series, that prevented this behaviour as it was flagged by our qa/security team as a security issue.
It’s probably just a good idea to solve it at the source and for everyone?
Issue Analytics
- State:
- Created 3 years ago
- Comments:32 (17 by maintainers)
Top Results From Across the Web
SimpleCouchbaseRepository.findOne() doesn't validate ...
SimpleCouchbaseRepository.findOne() doesn't validate _class types. ... but not properly initialized - no exceptions are raised.
Read more >SpringBoot CouchbaseTemplate: findbyId is working But ...
findById (Entity.class).one(id) is working fine, I am able to fetch data · findByQuery(Entity.class).all() returning empty array · findByQuery( ...
Read more >Spring Data Couchbase - Reference Documentation
If the domain class is annotated with the module-specific type annotation, then it's a valid candidate for the particular Spring Data module.
Read more >Index (Spring Data Couchbase 2.2.10.RELEASE API)
Returns an array containing the constants of this enum type, in the order they are declared. values() - Static method in enum org.springframework.data.couchbase ......
Read more >Intro to Spring Data Couchbase
First, we add the following Maven dependency to our pom.xml file: ... Another popular reason to override typeKey() is if you are using...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
You’re right it’s not directly related to security, but it’s used to enforce type safety. And bugs with type safety can lead to exposing sensitive information. So indirectly I assert that it IS a security concern.
Java is generally considered a type safe language (there are limits and arguments to be made) but it’s not javascript or ruby.
At the Java level when defining and using a
Repository
there is an explicit contract on the types that the repository will accept and produce.GroupRepository#save()
will only compile ifGroup
types are passed in.And
Repository#findById()
will only return instances ofGroup
.However it seems that although type information is persisted and maintained by
save()
it is ignored byfindById()
. Strangely enough iffindAll()
where to return documents of a different type, this would be considered a bug… after all the explicit type contract was neglected, was it not?In the context of a
Repository
findById()
is the outlier that does not enforce type safety. Why is it special?To be thorough I think
Repository#deleteById()
,#deleteAllById
,#existsById
also suffers from the same problem.Yes it is.
I also remember Couchbase Professional Services recommending that for micro services we have them share a bucket. Due to scaling limitations of Couchbase, If I remember correctly it performs really badly with more than 10 buckets. And we have 60 odd microservices at last count.
We do have a seperate security bucket for really sensitive information. So there is that.
But even within a single micro service it can and does in our case, contain multiple document types. Represented as distinct resources via a REST API.
And I agree with you that the micro service probably needs to enforce this, not Couchbase itself. I’m not arguing that it be handled by Couchbase for me. I’m just pointing out that Spring Data Couchbase is intended to be a high level interface to Couchbase which provides specific and different (from the raw SDK) paradigms for accessing it. I think the responsibility lies here.
I’m not discounting this use case, but I am saying I believe it’s definitely not valid in the context of a
Repository
.And I think we probably crossover into talking about polymorphism and how to represent that for Couchbase documents, in terms of ORMs JPA and Hibernate are probably a good base line here.
And to reenforce the argument, if one does not want type safety and wants to duck type documents, then they probably shouldn’t be Spring Data Couchbase? As it’s design makes the document types explicit unlike the underlying Couchbase SDK.
I assume you mean
GetResult#contentAs(OtherEntity.class)
?So
CouchbaseTemplate
has no specific generic type, and always requires the user to pass in the type they want returned. This makes sense, it’s at a lower level than theSimpleCouchbaseRepository
implementation.Given it does accept any class type in the
insertById()
method, and persist that class type into a field in the raw document. And there is a natural assumed symmetry betweeninsertById()
andfindById()
.I believe it should also allow one to enforce the type of the underlying documents. Maybe that’s by a configuration flag, or by a new explicit type safe method,
typeSafeFindById()
or perhapsfindById(Some.class).verifyType().one("123")
.But how?
By the time
CouchbaseTemplate#findById()
has returned, the type information is gone… lost.And if your answer is going to be, well you can just add another query method to your repository… as a work around. Right sure, so for all the ById methods in the
CouchbaseRepository
interface, I’ll re-implement them because the out of the box methods have broken the explicit type contract as defined in the repository class definition…I’m preemptively saying I think this argument is weak at best.
I believe that
CouchbaseTemplate
needs to provide this functionality out of the box.True enough.
I never wanted for applications to have to identify the type from the key 😃
We only originally did it for aiding human troubleshooting, when looking at logs and other output. Our previous system based on MsSql used the same approach for identifiers. And I think when Couchbase Processional services recommended it (some 4 years ago now), we didn’t think too hard about it. Certainly we never intended our applications to verify the prefix in any way.
What I really WANT is for Spring Data Couchbase to not violate the generic interface contract as defined in the repository interfaces, I want that type declaration to be honored for all methods in the interface, not just methods that are backed by N1QL.
I understand the KV store doesn’t make this easy to do. There is a mismatch between Couchbase’s capabilities and what Java tells you the type will be.
You’ve given me a workaround, which is wonderful.
But I think you really need to make others aware of this quirk by documenting it, and perhaps for those who want the same sanity document the same workaround you provided to me.
And if I think about it it seems the scopes and collections feature in the upcoming CB7 is an attempt to right this wrong. I.e. Using key prefixes isn’t ideal, and there’s probably a better way to do it… so try this approach.
Yes, we value composition over inheritance. Which is what I ultimately think your trying to say here. Not that you’re taking about inheritance per se.
But violating the type contract of a generic interface and us having to implement a Custom Converter to fix it? That’s not really a technology requirement specific to us… it’s applicable to all Java applications.
I remember when Java 5 came out and suddenly, you could apply a generic type to a
Collection
. And I’m sure we had code to fix that was broken by the idea of being able to assign a single type to aCollection
. When we were exploiting it to store many different types. These days you can declare aList
and store anything in it, but people rarely do, and it’s even frowned upon in polite society 😉So having said all that, let me take what you have provided and let me see how I can integrate it and if it is good enough for our purposes.
I would also like if you took this feedback and ensured when it comes time to integrate scopes and collections into the repositories that you can make them honor the declared type. Default a repositories collection to the simple name of the entity?