Files
pvAccess/src/client/pvAccess.cpp
Michael Davidsaver fa484a883a redo security (aka. access control)
Break up monolithic SecurityPlugin API into three parts.

1. Authentication.
2. Authorization.
3. Access Control decision.  Left to ChannelProviders
2019-01-12 15:14:19 -08:00

478 lines
16 KiB
C++

/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/reftrack.h>
#include <pv/valueBuilder.h>
#include <pv/epicsException.h>
#define epicsExportSharedSymbols
#include <pv/pvAccess.h>
#include <pv/security.h>
namespace pvd = epics::pvData;
namespace epics {
namespace pvAccess {
size_t Channel::num_instances;
const char* Channel::ConnectionStateNames[] = { "NEVER_CONNECTED", "CONNECTED", "DISCONNECTED", "DESTROYED" };
Channel::Channel() {REFTRACE_INCREMENT(num_instances);}
Channel::~Channel() {REFTRACE_DECREMENT(num_instances);}
std::string Channel::getRequesterName()
{
std::tr1::shared_ptr<ChannelRequester> req(getChannelRequester());
return req ? req->getRequesterName() : std::string("<Destroy'd Channel>");
}
void Channel::message(std::string const & message, epics::pvData::MessageType messageType)
{
std::tr1::shared_ptr<ChannelRequester> req(getChannelRequester());
if(req) {
req->message(message, messageType);
} else {
std::cerr<<epics::pvData::getMessageTypeName(messageType)
<<": on Destroy'd Channel \""<<getChannelName()
<<"\" : "<<message;
}
}
Channel::ConnectionState Channel::getConnectionState() { return CONNECTED; }
bool Channel::isConnected() { return getConnectionState()==CONNECTED; }
void Channel::getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField)
{
requester->getDone(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented")
,pvd::FieldConstPtr());
}
AccessRights Channel::getAccessRights(epics::pvData::PVField::shared_pointer const & pvField)
{
return readWrite;
}
namespace {
/* allow createChannelProcess() to use a ChannelPut w/o data */
struct Process2PutProxy : public ChannelProcess
{
struct Req : public ChannelPutRequester
{
const ChannelProcessRequester::weak_pointer requester; // was passed to createChannelProcess()
const std::tr1::weak_ptr<Process2PutProxy> operation; // enclosing Process2PutProxy
epicsMutex mutex;
epics::pvData::PVStructurePtr dummy;
Req(const ChannelProcessRequester::weak_pointer& req,
const std::tr1::weak_ptr<Process2PutProxy>& op)
:requester(req), operation(op)
{}
virtual ~Req() {}
virtual std::string getRequesterName() OVERRIDE FINAL {
ChannelProcessRequester::shared_pointer req(requester.lock());
return req ? req->getRequesterName() : "";
}
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL {
epics::pvData::PVStructurePtr dummy;
{
epicsGuard<epicsMutex> G(mutex);
this->dummy.swap(dummy);
}
ChannelProcessRequester::shared_pointer req(requester.lock());
if(req)
req->channelDisconnect(destroy);
}
virtual void channelPutConnect(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut,
epics::pvData::Structure::const_shared_pointer const & structure) OVERRIDE FINAL
{
epics::pvData::PVStructurePtr dummy(epics::pvData::getPVDataCreate()->createPVStructure(structure));
ChannelProcessRequester::shared_pointer req(requester.lock());
std::tr1::shared_ptr<Process2PutProxy> op(operation.lock());
if(!op) return;
{
epicsGuard<epicsMutex> G(mutex);
this->dummy = dummy;
op->op = channelPut;
}
if(req)
req->channelProcessConnect(status, op);
}
virtual void putDone(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut) OVERRIDE FINAL
{
ChannelProcessRequester::shared_pointer req(requester.lock());
std::tr1::shared_ptr<Process2PutProxy> op(operation.lock());
if(req && op)
req->processDone(status, op);
}
virtual void getDone(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut,
epics::pvData::PVStructure::shared_pointer const & pvStructure,
epics::pvData::BitSet::shared_pointer const & bitSet) OVERRIDE FINAL
{ /* never called */ }
};
ChannelPut::shared_pointer op; // the op we wrap
std::tr1::shared_ptr<Req> op_request; // keep our Req alive
epics::pvData::BitSetPtr empty;
Process2PutProxy() :empty(new epics::pvData::BitSet) {}
virtual ~Process2PutProxy() {}
virtual void destroy() OVERRIDE FINAL
{ op->destroy(); }
virtual std::tr1::shared_ptr<Channel> getChannel() OVERRIDE FINAL
{ return op->getChannel(); }
virtual void cancel() OVERRIDE FINAL
{ op->cancel(); }
virtual void lastRequest() OVERRIDE FINAL
{ op->lastRequest(); }
virtual void process() OVERRIDE FINAL
{
epics::pvData::PVStructurePtr blob;
{
epicsGuard<epicsMutex> G(op_request->mutex);
blob = op_request->dummy;
}
if(!blob) {
ChannelProcessRequester::shared_pointer req(op_request->requester.lock());
ChannelProcess::shared_pointer op(op_request->operation.lock());
req->processDone(epics::pvData::Status::error("Not connected"), op);
} else {
empty->clear();
op->put(blob, empty);
}
}
};
}//namespace
ChannelProcess::shared_pointer Channel::createChannelProcess(
ChannelProcessRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequestx)
{
pvd::PVStructure::shared_pointer pvRequest(pvRequestx);
std::tr1::shared_ptr<Process2PutProxy> ret(new Process2PutProxy);
ret->op_request.reset(new Process2PutProxy::Req(requester, ret));
// inject record._options.process=true if client hasn't provided
if(!pvRequest->getSubField("record._options.process"))
{
pvRequest = pvd::ValueBuilder(*pvRequest)
.addNested("record")
.addNested("_options")
.add<pvd::pvString>("process", "true")
.endNested()
.endNested()
.buildPVStructure();
}
ChannelPut::shared_pointer op(createChannelPut(ret->op_request, pvRequest));
if(!op) {
ret.reset();
} else {
epicsGuard<epicsMutex> G(ret->op_request->mutex);
ret->op = op;
}
return ret;
}
namespace {
/** Allow createChannelGet() to use createChannelPut()
*/
struct Get2PutProxy : public ChannelGet
{
struct Req : public ChannelPutRequester
{
const ChannelGetRequester::weak_pointer requester; // was passed to createChannelGet()
const std::tr1::weak_ptr<Get2PutProxy> operation; // enclosing Get2PutProxy
epicsMutex mutex;
Req(const ChannelGetRequester::weak_pointer& req,
const std::tr1::weak_ptr<Get2PutProxy>& op)
:requester(req), operation(op)
{}
virtual ~Req() {}
virtual std::string getRequesterName() OVERRIDE FINAL {
ChannelGetRequester::shared_pointer req(requester.lock());
return req ? req->getRequesterName() : "";
}
virtual void message(const std::string &message, MessageType messageType) OVERRIDE FINAL {
ChannelGetRequester::shared_pointer req(requester.lock());
if(req)
req->message(message, messageType);
else
ChannelPutRequester::message(message, messageType);
}
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL {
ChannelGetRequester::shared_pointer req(requester.lock());
if(req)
req->channelDisconnect(destroy);
}
virtual void channelPutConnect(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut,
epics::pvData::Structure::const_shared_pointer const & structure) OVERRIDE FINAL
{
ChannelGetRequester::shared_pointer req(requester.lock());
std::tr1::shared_ptr<Get2PutProxy> op(operation.lock());
if(!op) return;
{
epicsGuard<epicsMutex> G(mutex);
op->op = channelPut;
}
if(req)
req->channelGetConnect(status, op, structure);
}
virtual void putDone(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut) OVERRIDE FINAL
{ /* never called */ }
virtual void getDone(
const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut,
epics::pvData::PVStructure::shared_pointer const & pvStructure,
epics::pvData::BitSet::shared_pointer const & bitSet) OVERRIDE FINAL
{
ChannelGetRequester::shared_pointer req(requester.lock());
std::tr1::shared_ptr<Get2PutProxy> op(operation.lock());
if(req && op)
req->getDone(status, op, pvStructure, bitSet);
}
};
ChannelPut::shared_pointer op; // the put we wrap
std::tr1::shared_ptr<Get2PutProxy::Req> op_request; // keep our Req alive
ChannelPut::shared_pointer OP() {
epicsGuard<epicsMutex> G(op_request->mutex);
return op;
}
Get2PutProxy() {}
virtual ~Get2PutProxy() {}
virtual void destroy() OVERRIDE FINAL
{
ChannelPut::shared_pointer O(OP());
if(O) O->destroy();
}
virtual std::tr1::shared_ptr<Channel> getChannel() OVERRIDE FINAL
{
ChannelPut::shared_pointer O(OP());
return O ? O->getChannel() : std::tr1::shared_ptr<Channel>();
}
virtual void cancel() OVERRIDE FINAL
{
ChannelPut::shared_pointer O(OP());
if(O) O->cancel();
}
virtual void lastRequest() OVERRIDE FINAL
{
ChannelPut::shared_pointer O(OP());
if(O) O->lastRequest();
}
virtual void get() OVERRIDE FINAL
{
ChannelPut::shared_pointer O(OP());
if(O) O->get();
}
};
}// namespace
ChannelGet::shared_pointer Channel::createChannelGet(
ChannelGetRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
std::tr1::shared_ptr<Get2PutProxy> ret(new Get2PutProxy);
ret->op_request.reset(new Get2PutProxy::Req(requester, ret));
ChannelPut::shared_pointer op(createChannelPut(ret->op_request, pvRequest));
if(!op) {
ret.reset();
} else {
epicsGuard<epicsMutex> G(ret->op_request->mutex);
ret->op = op;
}
return ret;
}
ChannelPut::shared_pointer Channel::createChannelPut(
ChannelPutRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
ChannelPut::shared_pointer ret;
requester->channelPutConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented"),
ret, pvd::StructureConstPtr());
return ret;
}
ChannelPutGet::shared_pointer Channel::createChannelPutGet(
ChannelPutGetRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
ChannelPutGet::shared_pointer ret;
requester->channelPutGetConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented"),
ret, pvd::StructureConstPtr(), pvd::StructureConstPtr());
return ret;
}
ChannelRPC::shared_pointer Channel::createChannelRPC(
ChannelRPCRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
ChannelRPC::shared_pointer ret;
requester->channelRPCConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented"), ret);
return ret;
}
pvd::Monitor::shared_pointer Channel::createMonitor(
epics::pvAccess::MonitorRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
pvd::Monitor::shared_pointer ret;
requester->monitorConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented"),
ret, pvd::StructureConstPtr());
return ret;
}
ChannelArray::shared_pointer Channel::createChannelArray(
ChannelArrayRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
ChannelArray::shared_pointer ret;
requester->channelArrayConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented"),
ret, pvd::Array::const_shared_pointer());
return ret;
}
size_t ChannelProvider::num_instances;
ChannelProvider::ChannelProvider()
{
REFTRACE_INCREMENT(num_instances);
}
ChannelProvider::~ChannelProvider()
{
REFTRACE_DECREMENT(num_instances);
}
size_t ChannelBaseRequester::num_instances;
ChannelBaseRequester::ChannelBaseRequester()
{
REFTRACE_INCREMENT(num_instances);
}
ChannelBaseRequester::~ChannelBaseRequester()
{
REFTRACE_DECREMENT(num_instances);
}
size_t ChannelRequest::num_instances;
ChannelRequest::ChannelRequest()
{
REFTRACE_INCREMENT(num_instances);
}
ChannelRequest::~ChannelRequest()
{
REFTRACE_DECREMENT(num_instances);
}
size_t ChannelRequester::num_instances;
ChannelRequester::ChannelRequester()
{
REFTRACE_INCREMENT(num_instances);
}
ChannelRequester::~ChannelRequester()
{
REFTRACE_DECREMENT(num_instances);
}
PeerInfo::const_shared_pointer ChannelRequester::getPeerInfo()
{
return PeerInfo::const_shared_pointer();
}
std::string DefaultChannelRequester::getRequesterName() { return "DefaultChannelRequester"; }
void DefaultChannelRequester::channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel)
{
if(!status.isSuccess()) {
std::ostringstream strm;
status.dump(strm);
throw std::runtime_error(strm.str());
}
}
void DefaultChannelRequester::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState)
{ /* no-op */ }
ChannelRequester::shared_pointer DefaultChannelRequester::build()
{
ChannelRequester::shared_pointer ret(new DefaultChannelRequester);
return ret;
}
MonitorElement::MonitorElement(epics::pvData::PVStructurePtr const & pvStructurePtr)
: pvStructurePtr(pvStructurePtr)
,changedBitSet(epics::pvData::BitSet::create(static_cast<epics::pvData::uint32>(pvStructurePtr->getNumberFields())))
,overrunBitSet(epics::pvData::BitSet::create(static_cast<epics::pvData::uint32>(pvStructurePtr->getNumberFields())))
{}
}} // namespace epics::pvAccess
namespace {
struct DummyChannelFind : public epics::pvAccess::ChannelFind {
epics::pvAccess::ChannelProvider::weak_pointer provider;
DummyChannelFind(const epics::pvAccess::ChannelProvider::shared_pointer& provider) : provider(provider) {}
virtual ~DummyChannelFind() {}
virtual void destroy() OVERRIDE FINAL {}
virtual epics::pvAccess::ChannelProvider::shared_pointer getChannelProvider() OVERRIDE FINAL { return provider.lock(); }
virtual void cancel() OVERRIDE FINAL {}
};
}
namespace epics {namespace pvAccess {
ChannelFind::shared_pointer ChannelFind::buildDummy(const ChannelProvider::shared_pointer& provider)
{
std::tr1::shared_ptr<DummyChannelFind> ret(new DummyChannelFind(provider));
return ret;
}
}} // namespace epics::pvAccess