diff --git a/src/pv/channelProviderLocal.h b/src/pv/channelProviderLocal.h index be70650..1670e2f 100644 --- a/src/pv/channelProviderLocal.h +++ b/src/pv/channelProviderLocal.h @@ -27,6 +27,7 @@ #include #include +#include namespace epics { namespace pvDatabase { @@ -58,6 +59,19 @@ class epicsShareClass ChannelProviderLocal : { public: POINTER_DEFINITIONS(ChannelProviderLocal); + /** + * @brief Initialize access security configuration + * @param filePath AS definition file path + * @param substitutions macro substitutions + * @throws std::runtime_error in case of configuration problem + */ + static void initAs(const std::string& filePath, const std::string& substitutions=""); + /** + * @brief Is access security active? + * @return true is AS is active + */ + static bool isAsActive(); + /** * @brief Constructor */ @@ -158,6 +172,7 @@ private: friend class ChannelProviderLocalRun; }; + /** * @brief Channel for accessing a PVRecord. * @@ -341,6 +356,12 @@ public: * @param out the stream on which the message is displayed. */ virtual void printInfo(std::ostream& out); + /** + * @brief determines if client can write + * + * @return true if client can write + */ + virtual bool canWrite(); protected: shared_pointer getPtrSelf() { @@ -351,6 +372,19 @@ private: ChannelProviderLocalWPtr provider; PVRecordWPtr pvRecord; epics::pvData::Mutex mutex; + + // AS-specific variables/methods + std::vector toCharArray(const std::string& s); + std::vector getAsGroup(const PVRecordPtr& pvRecord); + std::vector getAsUser(const epics::pvAccess::ChannelRequester::shared_pointer& requester); + std::vector getAsHost(const epics::pvAccess::ChannelRequester::shared_pointer& requester); + + int asLevel; + std::vector asGroup; + std::vector asUser; + std::vector asHost; + ASMEMBERPVT asMemberPvt; + ASCLIENTPVT asClientPvt; }; }} diff --git a/src/pvAccess/channelLocal.cpp b/src/pvAccess/channelLocal.cpp index 0a71ee5..947d339 100644 --- a/src/pvAccess/channelLocal.cpp +++ b/src/pvAccess/channelLocal.cpp @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #define epicsExportSharedSymbols #include "pv/pvStructureCopy.h" @@ -517,6 +519,13 @@ void ChannelPutLocal::put( { ChannelPutRequester::shared_pointer requester = channelPutRequester.lock(); if(!requester) return; + ChannelLocalPtr channel(channelLocal.lock()); + if(!channel->canWrite()) { + Status status = Status::error("Channel put is not allowed"); + requester->putDone(status,getPtrSelf()); + return; + } + PVRecordPtr pvr(pvRecord.lock()); if(!pvr) throw std::logic_error("pvRecord is deleted"); try { @@ -677,6 +686,12 @@ void ChannelPutGetLocal::putGet( { ChannelPutGetRequester::shared_pointer requester = channelPutGetRequester.lock(); if(!requester) return; + ChannelLocalPtr channel(channelLocal.lock()); + if(!channel->canWrite()) { + Status status = Status::error("Channel putGet is not allowed"); + requester->putGetDone(status,getPtrSelf(),pvGetStructure,getBitSet); + return; + } PVRecordPtr pvr(pvRecord.lock()); if(!pvr) throw std::logic_error("pvRecord is deleted"); try { @@ -1225,7 +1240,13 @@ ChannelLocal::ChannelLocal( : requester(requester), provider(provider), - pvRecord(pvRecord) + pvRecord(pvRecord), + asLevel(pvRecord->getAsLevel()), + asGroup(getAsGroup(pvRecord)), + asUser(getAsUser(requester)), + asHost(getAsHost(requester)), + asMemberPvt(0), + asClientPvt(0) { if(pvRecord->getTraceLevel()>0) { cout << "ChannelLocal::ChannelLocal()" @@ -1233,11 +1254,86 @@ ChannelLocal::ChannelLocal( << " requester exists " << (requester ? "true" : "false") << endl; } + if (pvRecord->getAsGroup().empty() || asAddMember(&asMemberPvt, &asGroup[0]) != 0) { + asMemberPvt = 0; + } + if (asMemberPvt) { + asAddClient(&asClientPvt, asMemberPvt, asLevel, &asUser[0], &asHost[0]); + } +} + +std::vector ChannelLocal::toCharArray(const std::string& s) +{ + std::vector v(s.begin(), s.end()); + v.push_back('\0'); + return v; +} + +std::vector ChannelLocal::getAsGroup(const PVRecordPtr& pvRecord) +{ + return toCharArray(pvRecord->getAsGroup()); +} + +std::vector ChannelLocal::getAsUser(const ChannelRequester::shared_pointer& requester) +{ + PeerInfo::const_shared_pointer info(requester->getPeerInfo()); + std::string user; + if(info && info->identified) { + if(info->authority=="ca") { + user = info->account; + size_t first = user.find_last_of('/'); + if(first != std::string::npos) { + // prevent CA accounts like "/" + user = user.substr(first+1); + } + } + else { + user = info->authority + "/" + info->account; + } + } + return toCharArray(user); +} + +std::vector ChannelLocal::getAsHost(const epics::pvAccess::ChannelRequester::shared_pointer& requester) +{ + PeerInfo::const_shared_pointer info(requester->getPeerInfo()); + std::string host; + if(info && info->identified) { + host= info->peer; + } + else { + // anonymous + host = requester->getRequesterName(); + } + + // handle form "ip:port" + size_t last = host.find_first_of(':'); + if(last == std::string::npos) { + last = host.size(); + } + host.resize(last); + return toCharArray(host); +} + +bool ChannelLocal::canWrite() +{ + if(!asActive || (asClientPvt && asCheckPut(asClientPvt))) { + return true; + } + return false; } ChannelLocal::~ChannelLocal() { // cout << "~ChannelLocal()" << endl; + if(asMemberPvt) { + asRemoveMember(&asMemberPvt); + asMemberPvt = 0; + } + if(asClientPvt) { + asRemoveClient(&asClientPvt); + asClientPvt = 0; + } } ChannelProvider::shared_pointer ChannelLocal::getProvider() diff --git a/src/pvAccess/channelProviderLocal.cpp b/src/pvAccess/channelProviderLocal.cpp index 7b1b12b..38b029d 100644 --- a/src/pvAccess/channelProviderLocal.cpp +++ b/src/pvAccess/channelProviderLocal.cpp @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -176,4 +177,18 @@ Channel::shared_pointer ChannelProviderLocal::createChannel( return createChannel(channelName, channelRequester, priority); } +void ChannelProviderLocal::initAs(const std::string& filePath, const std::string& substitutions) +{ + int status = asInitFile(filePath.c_str(), substitutions.c_str()); + if(status) { + throw std::runtime_error("Invalid AS configuration."); + } +} + +bool ChannelProviderLocal::isAsActive() +{ + return asActive; +} + + }}