blob: dc67e89856e1ed2c699200912e2ada734ead60b2 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "Privilege.h"
#include "RequestPath.h"
#include "SubjectDescriptor.h"
#include "lib/support/CodeUtils.h"
#include <lib/core/CHIPCore.h>
namespace chip {
namespace Access {
class AccessControl
{
public:
/**
* Used by access control to determine if a device type resolves to an endpoint.
*/
struct DeviceTypeResolver
{
public:
virtual ~DeviceTypeResolver() = default;
virtual bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) = 0;
};
/**
* Handle to an entry in the access control list.
*
* Must be prepared (`AccessControl::PrepareEntry`) or read (`AccessControl::ReadEntry`) before first use.
*/
class Entry
{
public:
struct Target
{
using Flags = unsigned;
static constexpr Flags kCluster = 1 << 0;
static constexpr Flags kEndpoint = 1 << 1;
static constexpr Flags kDeviceType = 1 << 2;
Flags flags = 0;
ClusterId cluster;
EndpointId endpoint;
DeviceTypeId deviceType;
};
class Delegate
{
public:
Delegate() = default;
Delegate(const Delegate &) = delete;
Delegate & operator=(const Delegate &) = delete;
virtual ~Delegate() = default;
virtual void Release() {}
// Simple getters
virtual CHIP_ERROR GetAuthMode(AuthMode & authMode) const { return CHIP_NO_ERROR; }
virtual CHIP_ERROR GetFabricIndex(FabricIndex & fabricIndex) const { return CHIP_NO_ERROR; }
virtual CHIP_ERROR GetPrivilege(Privilege & privilege) const { return CHIP_NO_ERROR; }
// Simple setters
virtual CHIP_ERROR SetAuthMode(AuthMode authMode) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR SetFabricIndex(FabricIndex fabricIndex) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR SetPrivilege(Privilege privilege) { return CHIP_NO_ERROR; }
// Subjects
virtual CHIP_ERROR GetSubjectCount(size_t & count) const
{
count = 0;
return CHIP_NO_ERROR;
}
virtual CHIP_ERROR GetSubject(size_t index, NodeId & subject) const { return CHIP_NO_ERROR; }
virtual CHIP_ERROR SetSubject(size_t index, NodeId subject) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR AddSubject(size_t * index, NodeId subject) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR RemoveSubject(size_t index) { return CHIP_NO_ERROR; }
// Targets
virtual CHIP_ERROR GetTargetCount(size_t & count) const
{
count = 0;
return CHIP_NO_ERROR;
}
virtual CHIP_ERROR GetTarget(size_t index, Target & target) const { return CHIP_NO_ERROR; }
virtual CHIP_ERROR SetTarget(size_t index, const Target & target) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR AddTarget(size_t * index, const Target & target) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR RemoveTarget(size_t index) { return CHIP_NO_ERROR; }
};
Entry() = default;
Entry(Entry && other) : mDelegate(other.mDelegate) { other.mDelegate = &mDefaultDelegate; }
Entry & operator=(Entry && other)
{
if (this != &other)
{
mDelegate->Release();
mDelegate = other.mDelegate;
other.mDelegate = &mDefaultDelegate;
}
return *this;
}
Entry(const Entry &) = delete;
Entry & operator=(const Entry &) = delete;
~Entry() { mDelegate->Release(); }
// Simple getters
CHIP_ERROR GetAuthMode(AuthMode & authMode) const { return mDelegate->GetAuthMode(authMode); }
CHIP_ERROR GetFabricIndex(FabricIndex & fabricIndex) const { return mDelegate->GetFabricIndex(fabricIndex); }
CHIP_ERROR GetPrivilege(Privilege & privilege) const { return mDelegate->GetPrivilege(privilege); }
// Simple setters
CHIP_ERROR SetAuthMode(AuthMode authMode) { return mDelegate->SetAuthMode(authMode); }
CHIP_ERROR SetFabricIndex(FabricIndex fabricIndex) { return mDelegate->SetFabricIndex(fabricIndex); }
CHIP_ERROR SetPrivilege(Privilege privilege) { return mDelegate->SetPrivilege(privilege); }
/**
* Gets the number of subjects.
*
* @param [out] count The number of subjects.
*/
CHIP_ERROR GetSubjectCount(size_t & count) const { return mDelegate->GetSubjectCount(count); }
/**
* Gets the specified subject.
*
* @param [in] index The index of the subject to get.
* @param [out] subject The subject into which to get.
*/
CHIP_ERROR GetSubject(size_t index, NodeId & subject) const { return mDelegate->GetSubject(index, subject); }
/**
* Sets the specified subject.
*
* @param [in] index The index of the subject to set.
* @param [in] subject The subject from which to set.
*/
CHIP_ERROR SetSubject(size_t index, NodeId subject) { return mDelegate->SetSubject(index, subject); }
/**
* Adds the specified subject.
*
* @param [out] index The index of the added subject, if not null.
* @param [in] subject The subject to add.
*/
CHIP_ERROR AddSubject(size_t * index, NodeId subject) { return mDelegate->AddSubject(index, subject); }
/**
* Removes the specified subject.
*
* @param [in] index The index of the subject to delete.
*/
CHIP_ERROR RemoveSubject(size_t index) { return mDelegate->RemoveSubject(index); }
/**
* Gets the number of targets.
*
* @param [out] count The number of targets.
*/
CHIP_ERROR GetTargetCount(size_t & count) const { return mDelegate->GetTargetCount(count); }
/**
* Gets the specified target.
*
* @param [in] index The index of the target to get.
* @param [out] target The target into which to get.
*/
CHIP_ERROR GetTarget(size_t index, Target & target) const { return mDelegate->GetTarget(index, target); }
/**
* Sets the specified target.
*
* @param [in] index The index of the target to set.
* @param [in] target The target from which to set.
*/
CHIP_ERROR SetTarget(size_t index, const Target & target) { return mDelegate->SetTarget(index, target); }
/**
* Adds the specified target.
*
* @param [out] index The index of the added target, if not null.
* @param [in] target The target to add.
*/
CHIP_ERROR AddTarget(size_t * index, const Target & target) { return mDelegate->AddTarget(index, target); }
/**
* Removes the specified target.
*
* @param [in] index The index of the target to delete.
*/
CHIP_ERROR RemoveTarget(size_t index) { return mDelegate->RemoveTarget(index); }
public:
const Delegate & GetDelegate() const { return *mDelegate; }
Delegate & GetDelegate() { return *mDelegate; }
void SetDelegate(Delegate & delegate)
{
mDelegate->Release();
mDelegate = &delegate;
}
void ResetDelegate()
{
mDelegate->Release();
mDelegate = &mDefaultDelegate;
}
private:
static Delegate mDefaultDelegate;
Delegate * mDelegate = &mDefaultDelegate;
};
/**
* Handle to an entry iterator in the access control list.
*
* Must be initialized (`AccessControl::Entries`) before first use.
*/
class EntryIterator
{
public:
class Delegate
{
public:
Delegate() = default;
Delegate(const Delegate &) = delete;
Delegate & operator=(const Delegate &) = delete;
virtual ~Delegate() = default;
virtual void Release() {}
virtual CHIP_ERROR Next(Entry & entry) { return CHIP_ERROR_SENTINEL; }
};
EntryIterator() = default;
EntryIterator(const EntryIterator &) = delete;
EntryIterator & operator=(const EntryIterator &) = delete;
~EntryIterator() { mDelegate->Release(); }
CHIP_ERROR Next(Entry & entry) { return mDelegate->Next(entry); }
public:
const Delegate & GetDelegate() const { return *mDelegate; }
Delegate & GetDelegate() { return *mDelegate; }
void SetDelegate(Delegate & delegate)
{
mDelegate->Release();
mDelegate = &delegate;
}
void ResetDelegate()
{
mDelegate->Release();
mDelegate = &mDefaultDelegate;
}
private:
static Delegate mDefaultDelegate;
Delegate * mDelegate = &mDefaultDelegate;
};
class Extension
{
// TODO: implement extension
};
class ExtensionIterator
{
// TODO: implement extension iterator
};
class Listener
{
public:
virtual ~Listener() = default;
// TODO: add entry/extension to listener interface
virtual void OnEntryChanged() = 0;
virtual void OnExtensionChanged() = 0;
};
class Delegate
{
public:
Delegate() = default;
Delegate(const Delegate &) = delete;
Delegate & operator=(const Delegate &) = delete;
virtual ~Delegate() = default;
virtual void Release() {}
virtual CHIP_ERROR Init() { return CHIP_NO_ERROR; }
virtual CHIP_ERROR Finish() { return CHIP_NO_ERROR; }
// Capabilities
virtual CHIP_ERROR GetMaxEntryCount(size_t & value) const
{
value = 0;
return CHIP_NO_ERROR;
}
// TODO: add more capabilities
// Actualities
virtual CHIP_ERROR GetEntryCount(size_t & value) const
{
value = 0;
return CHIP_NO_ERROR;
}
// Preparation
virtual CHIP_ERROR PrepareEntry(Entry & entry) { return CHIP_NO_ERROR; }
// CRUD
virtual CHIP_ERROR CreateEntry(size_t * index, const Entry & entry, FabricIndex * fabricIndex) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR ReadEntry(size_t index, Entry & entry, const FabricIndex * fabricIndex) const { return CHIP_NO_ERROR; }
virtual CHIP_ERROR UpdateEntry(size_t index, const Entry & entry, const FabricIndex * fabricIndex) { return CHIP_NO_ERROR; }
virtual CHIP_ERROR DeleteEntry(size_t index, const FabricIndex * fabricIndex) { return CHIP_NO_ERROR; }
// Iteration
virtual CHIP_ERROR Entries(EntryIterator & iterator, const FabricIndex * fabricIndex) const { return CHIP_NO_ERROR; }
// Check
// Return CHIP_NO_ERROR if allowed, CHIP_ERROR_ACCESS_DENIED if denied,
// CHIP_ERROR_NOT_IMPLEMENTED to use the default check algorithm (against entries),
// or any other CHIP_ERROR if another error occurred.
virtual CHIP_ERROR Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
Privilege requestPrivilege)
{
return CHIP_ERROR_ACCESS_DENIED;
}
// Listening
virtual void SetListener(Listener & listener) { mListener = &listener; }
virtual void ClearListener() { mListener = nullptr; }
private:
Listener * mListener = nullptr;
};
AccessControl() = default;
AccessControl(const AccessControl &) = delete;
AccessControl & operator=(const AccessControl &) = delete;
~AccessControl()
{
// Never-initialized AccessControl instances will not have the delegate set.
if (IsInitialized())
{
mDelegate->Release();
}
}
/**
* Initialize the access control module. Must be called before first use.
*
* @return CHIP_NO_ERROR on success, CHIP_ERROR_INCORRECT_STATE if called more than once,
* CHIP_ERROR_INVALID_ARGUMENT if delegate is null, or other fatal error.
*/
CHIP_ERROR Init(AccessControl::Delegate * delegate, DeviceTypeResolver & deviceTypeResolver);
/**
* Deinitialize the access control module. Must be called when finished.
*/
CHIP_ERROR Finish();
// Capabilities
CHIP_ERROR GetMaxEntryCount(size_t & value) const
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->GetMaxEntryCount(value);
}
// Actualities
CHIP_ERROR GetEntryCount(size_t & value) const
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->GetEntryCount(value);
}
/**
* Prepares an entry.
*
* An entry must be prepared or read (`ReadEntry`) before first use.
*
* @param [in] entry Entry to prepare.
*/
CHIP_ERROR PrepareEntry(Entry & entry)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->PrepareEntry(entry);
}
/**
* Creates an entry in the access control list.
*
* @param [out] index Entry index of created entry, if not null. May be relative to `fabricIndex`.
* @param [in] entry Entry from which to copy.
* @param [out] fabricIndex Fabric index of created entry, if not null, in which case entry `index` will be relative to fabric.
*/
CHIP_ERROR CreateEntry(size_t * index, const Entry & entry, FabricIndex * fabricIndex = nullptr)
{
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->CreateEntry(index, entry, fabricIndex);
}
/**
* Reads an entry from the access control list.
*
* @param [in] index Entry index of entry to read. May be relative to `fabricIndex`.
* @param [out] entry Entry into which to copy.
* @param [in] fabricIndex Fabric to which entry `index` is relative, if not null.
*/
CHIP_ERROR ReadEntry(size_t index, Entry & entry, const FabricIndex * fabricIndex = nullptr) const
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->ReadEntry(index, entry, fabricIndex);
}
/**
* Updates an entry in the access control list.
*
* @param [in] index Entry index of entry to update, if not null. May be relative to `fabricIndex`.
* @param [in] entry Entry from which to copy.
* @param [in] fabricIndex Fabric to which entry `index` is relative, if not null.
*/
CHIP_ERROR UpdateEntry(size_t index, const Entry & entry, const FabricIndex * fabricIndex = nullptr)
{
ReturnErrorCodeIf(!IsValid(entry), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->UpdateEntry(index, entry, fabricIndex);
}
/**
* Deletes an entry from the access control list.
*
* @param [in] index Entry index of entry to delete. May be relative to `fabricIndex`.
* @param [in] fabricIndex Fabric to which entry `index` is relative, if not null.
*/
CHIP_ERROR DeleteEntry(size_t index, const FabricIndex * fabricIndex = nullptr)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->DeleteEntry(index, fabricIndex);
}
CHIP_ERROR RemoveFabric(FabricIndex fabricIndex);
/**
* Iterates over entries in the access control list.
*
* @param [out] iterator Iterator controlling the iteration.
* @param [in] fabricIndex Iteration is confined to fabric, if not null.
*/
CHIP_ERROR Entries(EntryIterator & iterator, const FabricIndex * fabricIndex = nullptr) const
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return mDelegate->Entries(iterator, fabricIndex);
}
/**
* Check whether access (by a subject descriptor, to a request path,
* requiring a privilege) should be allowed or denied.
*
* @retval #CHIP_ERROR_ACCESS_DENIED if denied.
* @retval other errors should also be treated as denied.
* @retval #CHIP_NO_ERROR if allowed.
*/
CHIP_ERROR Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, Privilege requestPrivilege);
private:
bool IsInitialized() const { return (mDelegate != nullptr); }
bool IsValid(const Entry & entry);
Delegate * mDelegate = nullptr;
DeviceTypeResolver * mDeviceTypeResolver = nullptr;
};
/**
* Get the global instance set by SetAccessControl, or the default.
*
* Calls to this function must be synchronized externally.
*/
AccessControl & GetAccessControl();
/**
* Set the global instance returned by GetAccessControl.
*
* Calls to this function must be synchronized externally.
*/
void SetAccessControl(AccessControl & accessControl);
/**
* Reset the global instance to the default.
*
* Calls to this function must be synchronized externally.
*/
void ResetAccessControl();
} // namespace Access
} // namespace chip