Is this key-oriented access-protection pattern a known idiom?
Asked Answered
M

4

57

Matthieu M. brought up a pattern for access-protection in this answer that i'd seen before, but never conciously considered a pattern:

class SomeKey { 
    friend class Foo;
    SomeKey() {} 
    // possibly make it non-copyable too
};

class Bar {
public:
    void protectedMethod(SomeKey);
};

Here only a friend of the key class has access to protectedMethod():

class Foo {
    void do_stuff(Bar& b) { 
        b.protectedMethod(SomeKey()); // fine, Foo is friend of SomeKey
    }
};

class Baz {
    void do_stuff(Bar& b) {
        b.protectedMethod(SomeKey()); // error, SomeKey::SomeKey() is private
    }
};

It allows more fine-granular access-control than making Foo a friend of Bar and avoids more complicated proxying patterns.

Does anyone know whether this approach already has a name, i.e., is a known pattern?

Minnesinger answered 10/7, 2010 at 16:43 Comment(15)
Might be an idea to show how you would actually use those classes.Altheaalthee
@Neil: Right, that may not be obvious.Minnesinger
It might be useful to make the key noncopyable, unless you want Foo to be able to delegate access to other classes (of course, delegation might be useful, depending on the circumstances).Pillsbury
Personally I don't like 'friend xxx' in general, but that's just me :-)Johnsonian
This is quite a neat idea for granting partial friendship... I don't have a C++ compiler handy, but I wonder if you could you make protectedMethod a template specialization, so that you don't even incur the cost of the SomeKey() construction...Maureen
@brone: Any decent compiler will optimize it away if the definition is visible. It only needs to behave "as if" it happened - which is pretty trivial for an empty class :)Minnesinger
Q: could it be defeated with the following hack? char* some_junk_object=new char[...]; SomeKey* p=(SomeKey*)(some_junk_object); b.protectedMethod(*p); If all you're doing is relying on the type system, this is trivial to subvert. (I've seen a similar key system fail spectacularly in a similar way.)Auschwitz
@Owen: If the copy-ctor is accessible, yes - but then again there is no way to protect against everything in C++, there is always some hack around it. We want to protect against mistakes, not deliberate abuse. If we don't want delegation and make the copy-ctor non-public, your hack wouldn't work.Minnesinger
Ah, yes, missed the copy – that will help. Just so long as you realize the extent to which your key may or may not protect you.Auschwitz
@Owen: I'm not sure that your question is really relevant. Of course you can (most probably) bypass it like you can bypass the normal access checks by doing stupid things like #define private public. As always it is the responsability of the programmer to use his tools correctly which is mostly what you said in your last comment.Heder
@nick: I'm trying to pass along that I've seen schemes like this fail without going to such lengths as altering the header's contents through editing or macro magic. It miffs me that you feel a caution on the idiom being described is irrelevant to the question, whether or not you think it's a valid criticism, but do with this as you see fit.Auschwitz
I opened a follow-up question on naming.Minnesinger
And a follow up on generalizing it.Chatav
Are there any anecdotes of this idiom being optimized into no-ops on GCC?Lam
Wonderful idiom. Thanks!Itinerant
R
16

Thanks to your other question it looks like this pattern is now known as the "passkey" pattern.

In C++11, it gets even cleaner, because instead of calling

b.protectedMethod(SomeKey());

you can just call:

b.protectedMethod({});
Reshape answered 28/4, 2014 at 6:11 Comment(4)
not sure, but without c++11 it could be an option to provide a SomeKey constructor that takes eg. an int to allow for a somewhat shorter b.protectedMethod(0);Witmer
Just a detail: The shorthand version foo.bar({}) doesn't work with constructors e.g. Foo foo({}) as it is ambiguous - the compiler doesn't know which constructor is being called, it could be a copy constructor. Or at least that's an explanation that seems reasonable to me, correct me if I'm wrong.Coreencorel
Can someone elaborate what means {} in b.protectedMethod({});Pastose
@yako: {} for default construct first argumentEduardo
C
7

It seems that this idiom like one mentioned in another SO question here. It is called Attorney-Client idiom and described in more details there.

Copley answered 10/7, 2010 at 17:22 Comment(4)
I already linked to another answer to that question and the pattern above doesn't involve proxying through another class. There is no "attorney" we need to call through (they are expensive anyway), instead we simply get access by passing a key along.Minnesinger
@Georg: In what real sense are the attorney calls expensive? If they are stateless, static inlines that merely forward arguments, how would any size or time penalty be introduced?Kei
@jeff: The point on expenses was meant to be a pun regarding hourly rates ;) Still, one has to manually forward in the attorney case - you have to write more.Minnesinger
@Georg: sorry, read pun as double entente on the call expense, not on method definition. I generally agree, but I did make a comment in the other question's thread about actual runtime expense.Kei
A
3

some boring man like me would make the fowllow code:

int FraudKey=0;
b.protectedMethod(reinterpret_cast<SomeKey&>(FraudKey));
Anceline answered 19/9, 2014 at 2:11 Comment(4)
That's not really surprising in C++ - this is not supposed to protect against malicious intent.Minnesinger
Maybe making SomeKey inner private to Bar would kind of protect against such uses - or at least restrict it to the Bar class. But then it would require to always use protectedMethod({}).Vinylidene
You have invoked undefined behavior. The bug exists in your code, as the caller of the function, because you are using reinterpret_cast to access an object by a type other than its actual type. From undefined behavior, absurdity follows.Complicacy
That's why the SomeKey copy ctor has to be private as well. That ensures that this would fail to compile, while valid uses would succeed.Sciolism
E
0

Its pretty close to this:

http://minorfs.wordpress.com/2013/01/18/raiicap-pattern-injected-singleton-alternative-for-c/

Basically if you consider a reference to an object of well designed class to be provide the access control you need to implement any access control policy that actually makes sense, applying this pattern to anything other than the constructor does not seem to make that much sense.

So as the article states, if you use this key in conjunction with those constructors for what access control might make sense, objects that represent significant parts of scares resources, that in C++ would generally be implemented as RAII objects, than the name RAIICap or RAII-Capability would indeed make sense.

http://www.eros-os.org/essays/capintro.html

Alternatively you could refer to it with a more general name like construct authority.

The implementation in the article is a bit to much main centered, that is, main needs to create all the authority keys. You can extend on it and make it more flexible by adding an additional public constructor for the key itself:

template <typename T>
class construct_authority {
  public:
    construct_authority(construct_authority<void> const&)
    friend int main(int,char **);
  private:
    construct_authority(){}
};

That way main could delegate the key creation to other parts of the program.

Personally I think the RAIICap name is quite appropriate for the useful part of this pattern.

A while ago I proposed that this simple template above could be added to the standard library.

https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/p_v-aYIvO1E

Unfortunately there are issues with the idea that there can be one main fingerprint that constitutes a computational root, so something like this apparently can't have a place in the standard library. Having said this, at least for the use with the constructor of RAII classes, this pattern seems to be quite useful.

Estimative answered 18/12, 2014 at 9:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.