/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// aclsubject - abstract ACL subject implementation
//
#ifndef _ACLSUBJECT
#define _ACLSUBJECT

#include <security_cdsa_utilities/cssmaclpod.h>
#include <security_cdsa_utilities/cssmcred.h>
#include <security_utilities/refcount.h>
#include <security_utilities/globalizer.h>
#include <security_utilities/memutils.h>
#include <security_utilities/adornments.h>
#include <map>
#include <set>
#include <string>
#include <limits.h>


namespace Security {

class AclValidationContext;
class AclSubject;


//
// An AclValidationEnvironment can be subclassed to add context access to ACL subject
// validation. If you use ACL subject classes that need context beyond the credential
// structure itself, add that context to (a virtual subclass of) AclValidationContext, pass that
// to ObjectAcl::validate() along with the credentials, and have the Subject implementation
// access validationContext.environment().
//
class AclValidationEnvironment {
	friend class AclValidationContext;
public:
    virtual ~AclValidationEnvironment();	// ensure virtual methods (need dynamic_cast)
	
	// provide an Adornable for a given subject to store data in, or throw if none available (default)
	virtual Adornable &store(const AclSubject *subject);
};


//
// An AclValidationContext holds all context for an ACL evaluation in one
// package. It's designed to provide a uniform representation of credentials, plus
// any (trusted path and/or implicit) context information useful for ACL validation.
//
// Contexts are immutable (constant) for validators; they do not change at all
// during a validation exercise. Anything that should be mutable must go into
// the environment (which is indirect and modifyable).
//
class AclValidationContext {
public:
    AclValidationContext(const AccessCredentials *cred,
        AclAuthorization auth, AclValidationEnvironment *env = NULL)
    : mCred(cred), mAuth(auth), mEnv(env), mEntryTag(NULL) { }
    AclValidationContext(const AclValidationContext &ctx)
    : mCred(ctx.mCred), mAuth(ctx.mAuth), mEnv(ctx.mEnv), mEntryTag(NULL) { }
	virtual ~AclValidationContext();

    // access to (suitably focused) sample set
    virtual uint32 count() const = 0;	// number of samples
	uint32 size() const { return count(); } // alias
    virtual const TypedList &sample(uint32 n) const = 0;	// retrieve one sample
    const TypedList &operator [] (uint32 n) const { return sample(n); }
    
    // context access
    AclAuthorization authorization() const	{ return mAuth; }
	const AccessCredentials *cred() const	{ return mCred; }
	AclValidationEnvironment *environment() const { return mEnv; }
    template <class Env> Env *environment() const { return dynamic_cast<Env *>(mEnv); }

	virtual const char *credTag() const;
	virtual const char *entryTag() const;
	std::string s_credTag() const;
	void entryTag(const char *tag);
	void entryTag(const std::string &tag);

    //@@@ add certificate access functions
    //@@@ add callback management
	
	virtual void matched(const TypedList *match) const = 0;
	void matched(const TypedList &match) const { return matched(&match); }

private:
    const AccessCredentials *mCred;		// original credentials
    AclAuthorization mAuth;				// action requested
    AclValidationEnvironment *mEnv;		// environmental context (if any)
	const char *mEntryTag;				// entry tag
};


//
// The AclSubject class models an ACL "subject" object. If you have a new ACL
// subject type or variant, you make a subclass of this (plus a suitable Maker).
//
// Note that AclSubjects can contain both configuration and state information.
// Configuration is set during AclSubject creation (passwords to check against,
// evaluation options, etc.) and are typically passed on in the externalized form;
// it is persistent.
// On the other hand, state is volatile and is lost when the AclSubject dies.
// This is stuff that accumulates during a particular lifetime, such as results
// of previous evaluations (for caching or more nefarious purposes).
// Be clear what each of your subclass members are, and document accordingly.
//
class AclSubject : public RefCount {
public:
    typedef LowLevelMemoryUtilities::Writer Writer;
    typedef LowLevelMemoryUtilities::Reader Reader;
	
	typedef uint8 Version;		// binary version marker
	static const int versionShift = 24;	// highest-order byte of type is version
	static const uint32 versionMask = 0xff000000;

public:
	explicit AclSubject(uint32 type, Version v = 0);
    virtual ~AclSubject();
    CSSM_ACL_SUBJECT_TYPE type() const { return mType; }
    
	// validation (evaluation) primitive
    virtual bool validate(const AclValidationContext &ctx) const = 0;
    
    // export to CSSM interface
    virtual CssmList toList(Allocator &alloc) const = 0;
        
    // export/import for save/restore interface
    virtual void exportBlob(Writer::Counter &pub, Writer::Counter &priv);
    virtual void exportBlob(Writer &pub, Writer &priv);
    virtual void importBlob(Reader &pub, Reader &priv);
	
	// binary compatibility version management. The version defaults to zero
	Version version() const	{ return mVersion; }
	
	// forget any validation-related state you have acquired
	virtual void reset();
	
	// debug suupport (dummied out but present for -UDEBUGDUMP)
	virtual void debugDump() const;
	
protected:
	void version(Version v)	{ mVersion = v; }
    
private:
    CSSM_ACL_SUBJECT_TYPE mType;
	Version mVersion;
    
public:
    class Maker {
    public:
        Maker(CSSM_ACL_SUBJECT_TYPE type);
		virtual ~Maker();
        
        uint32 type() const { return mType; }
        virtual AclSubject *make(const TypedList &list) const = 0;
        virtual AclSubject *make(Version version, Reader &pub, Reader &priv) const = 0;
		
    protected:
        // list parsing helpers
        static void crack(const CssmList &list, uint32 count,
            ListElement **array = NULL, ...);
        static CSSM_WORDID_TYPE getWord(const ListElement &list,
            int min = 0, int max = INT_MAX);
		
    private:
        CSSM_ACL_SUBJECT_TYPE mType;
    };
};


//
// A SimpleAclSubject validates a credential by scanning its samples
// one at a time, without any interactions between them. Thus its validate()
// can be a lot simpler.
// Note that this layer assumes that subject and sample types have the same
// value, as is typical when both are derived from a WORDID.
//
class SimpleAclSubject : public AclSubject {
public:
    SimpleAclSubject(CSSM_ACL_SUBJECT_TYPE type) : AclSubject(type) { }
    
    bool validate(const AclValidationContext &ctx) const;
    virtual bool validate(const AclValidationContext &baseCtx,
        const TypedList &sample) const = 0;
};


} // end namespace Security


#endif //_ACLSUBJECT
