/* init.c - RBAC initialization */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 */

#include "portable.h"

#include <stdio.h>

#include <ac/string.h>

#include "slap.h"
#include "slap-config.h"
#include "lutil.h"

#include "rbac.h"

static slap_callback nullsc = { NULL, NULL, NULL, NULL };

struct slap_rbac_internal_schema slap_rbac_schema;

extern rbac_tenant_t rbac_tenants;
extern int initialize_jts( void );

rbac_ad_t rbac_session_ads[] = {
	{ RBAC_SESSION_ID,
		BER_BVC("rbacSessid"), &slap_rbac_schema.ad_session_id },
	{ RBAC_USER_DN,
		BER_BVC("rbacUserDN"), &slap_rbac_schema.ad_session_user_dn },
	{ RBAC_ROLES,
		BER_BVC("rbacRoles"), &slap_rbac_schema.ad_session_roles },
	{ RBAC_ROLE_CONSTRAINTS,
		BER_BVC("rbacRoleConstraints"),
		&slap_rbac_schema.ad_session_role_constraints },
	{ RBAC_UID,
		BER_BVC("uid"), &slap_rbac_schema.ad_uid},
	{ RBAC_TENANT_ID,
		BER_BVC("tenantid"), &slap_rbac_schema.ad_tenant_id },

	{ RBAC_NONE, BER_BVNULL, NULL }
};

rbac_ad_t rbac_session_permission_ads[] = {
	{ RBAC_OP_NAME,
		BER_BVC("rbacOpName"), &slap_rbac_schema.ad_permission_opname },
	{ RBAC_OBJ_NAME,
		BER_BVC("rbacObjName"), &slap_rbac_schema.ad_permission_objname },
	{ RBAC_ROLE_NAME,
		BER_BVC("rbacRoleName"), &slap_rbac_schema.ad_permission_rolename },

	{ RBAC_NONE, BER_BVNULL, NULL }
};

rbac_ad_t audit_ads[] = {
	{ RBAC_AUDIT_OP,
		BER_BVC("rbacAuditOp"), &slap_rbac_schema.ad_audit_op },
	{ RBAC_AUDIT_ID,
		BER_BVC("rbacAuditId"), &slap_rbac_schema.ad_audit_id },
	{ RBAC_AUDIT_ROLES,
		BER_BVC("rbacAuditRoles"), &slap_rbac_schema.ad_audit_roles },
	{ RBAC_AUDIT_REQUESTED_ROLES,
		BER_BVC("rbacAuditRequestedRoles"),
		&slap_rbac_schema.ad_audit_requested_roles
	},
	{ RBAC_AUDIT_TIMESTAMP,
		BER_BVC("rbacAuditTimestamp"), &slap_rbac_schema.ad_audit_timestamp },
	{ RBAC_AUDIT_RESOURCES,
		BER_BVC("rbacAuditResources"), &slap_rbac_schema.ad_audit_resources },
	{ RBAC_AUDIT_OBJS,
		BER_BVC("rbacAuditObjects"), &slap_rbac_schema.ad_audit_objects },
	{ RBAC_AUDIT_OPS,
		BER_BVC("rbacAuditOperations"), &slap_rbac_schema.ad_audit_operations },
	{ RBAC_AUDIT_RESULT,
		BER_BVC("rbacAuditResult"), &slap_rbac_schema.ad_audit_result },
	{ RBAC_AUDIT_PROPERTIES,
		BER_BVC("rbacAuditProperties"), &slap_rbac_schema.ad_audit_properties },
	{ RBAC_AUDIT_MSGS,
		BER_BVC("rbacAuditMessages"), &slap_rbac_schema.ad_audit_messages },

	{ RBAC_NONE, BER_BVNULL, NULL }
};

/* initialize repository attribute descriptions */

static int
initialize_sessions()
{
	int i, nattrs, rc = LDAP_SUCCESS;
	const char *text;

	for ( nattrs = 0; !BER_BVISNULL( &rbac_session_ads[nattrs].attr );
			nattrs++ )
		; /* count the number of attrs */

	slap_rbac_schema.session_attrs =
			slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );

	for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
		rc = slap_bv2ad(
				&rbac_session_ads[i].attr, rbac_session_ads[i].ad, &text );
		if ( rc != LDAP_SUCCESS ) {
			goto done;
		}
		slap_rbac_schema.session_attrs[i].an_name = rbac_session_ads[i].attr;
		slap_rbac_schema.session_attrs[i].an_desc = *rbac_session_ads[i].ad;
	}

	BER_BVZERO( &slap_rbac_schema.session_attrs[nattrs].an_name );

done:;
	return rc;
}

static int
initialize_audit()
{
	int i, rc = LDAP_SUCCESS;
	const char *text;

	/* for audit */
	for ( i = 0; !BER_BVISNULL( &audit_ads[i].attr ); i++ ) {
		rc = slap_bv2ad( &audit_ads[i].attr, audit_ads[i].ad, &text );
		if ( rc != LDAP_SUCCESS ) {
			goto done;
		}
	}

done:;
	return rc;
}

static int
initialize_tenant(
		BackendDB *be,
		ConfigReply *cr,
		tenant_info_t *tenantp,
		int init_op )
{
	int rc = LDAP_SUCCESS;
	Entry *e = NULL;
	OperationBuffer opbuf;
	Operation *op2;
	SlapReply rs2 = { REP_RESULT };
	Connection conn = { 0 };
	struct berval rbac_container_oc = BER_BVC("rbacContainer");
	struct berval rbac_audit_container = BER_BVC("audit");
	struct berval rbac_session_container = BER_BVC("rbac");
	void *thrctx = ldap_pvt_thread_pool_context();

	e = entry_alloc();

	switch ( init_op ) {
		case INIT_AUDIT_CONTAINER:
			ber_dupbv( &e->e_name, &tenantp->audit_basedn );
			ber_dupbv( &e->e_nname, &tenantp->audit_basedn );

			/* container cn */
			attr_merge_one(
					e, slap_schema.si_ad_cn, &rbac_audit_container, NULL );
			break;
		case INIT_SESSION_CONTAINER:
			ber_dupbv( &e->e_name, &tenantp->sessions_basedn );
			ber_dupbv( &e->e_nname, &tenantp->sessions_basedn );

			/* rendered dynmaicObject for session */
			attr_merge_one( e, slap_schema.si_ad_objectClass,
					&slap_schema.si_oc_dynamicObject->soc_cname, NULL );

			/* container cn */
			attr_merge_one(
					e, slap_schema.si_ad_cn, &rbac_session_container, NULL );
			break;
		default:
			break;
	}

	attr_merge_one(
			e, slap_schema.si_ad_objectClass, &rbac_container_oc, NULL );
	attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
			&rbac_container_oc, NULL );

	/* store RBAC session */
	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
	op2 = &opbuf.ob_op;
	op2->o_callback = &nullsc;
	op2->o_tag = LDAP_REQ_ADD;
	op2->o_protocol = LDAP_VERSION3;
	op2->o_req_dn = e->e_name;
	op2->o_req_ndn = e->e_nname;
	op2->ora_e = e;
	op2->o_bd = select_backend( &op2->o_req_ndn, 0 );
	op2->o_dn = op2->o_bd->be_rootdn;
	op2->o_ndn = op2->o_bd->be_rootndn;
	rc = op2->o_bd->be_add( op2, &rs2 );

	if ( e ) entry_free( e );

	return rc;
}

int
rbac_initialize_tenants( BackendDB *be, ConfigReply *cr )
{
	int rc = LDAP_SUCCESS;
	rbac_tenant_t *tenantp = NULL;

	for ( tenantp = &rbac_tenants; tenantp; tenantp = tenantp->next ) {
		rc = initialize_tenant(
				be, cr, &tenantp->tenant_info, INIT_AUDIT_CONTAINER );
		if ( rc != LDAP_SUCCESS ) {
			if ( rc == LDAP_ALREADY_EXISTS ) {
				Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
						"audit container exists, tenant (%s)\n",
						tenantp->tenant_info.tid.bv_val ?
								tenantp->tenant_info.tid.bv_val :
								"NULL" );
				rc = LDAP_SUCCESS;
			} else {
				Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
						"failed to initialize (%s): rc (%d)\n",
						tenantp->tenant_info.tid.bv_val ?
								tenantp->tenant_info.tid.bv_val :
								"NULL",
						rc );
				goto done;
			}
		} else {
			Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
					"created audit container for tenant (%s):\n",
					tenantp->tenant_info.tid.bv_val ?
							tenantp->tenant_info.tid.bv_val :
							"NULL" );
		}
		rc = initialize_tenant(
				be, cr, &tenantp->tenant_info, INIT_SESSION_CONTAINER );
		if ( rc != LDAP_SUCCESS ) {
			if ( rc == LDAP_ALREADY_EXISTS ) {
				Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
						"session container exists, tenant (%s)\n",
						tenantp->tenant_info.tid.bv_val ?
								tenantp->tenant_info.tid.bv_val :
								"NULL" );
				rc = LDAP_SUCCESS;
			} else {
				Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
						"failed to initialize (%s): rc (%d)\n",
						tenantp->tenant_info.tid.bv_val ?
								tenantp->tenant_info.tid.bv_val :
								"NULL",
						rc );
				goto done;
			}
		} else {
			Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
					"created session container for tenant (%s):\n",
					tenantp->tenant_info.tid.bv_val ?
							tenantp->tenant_info.tid.bv_val :
							"NULL" );
		}
	}

done:;

	return rc;
}

static int
initialize_rbac_session_permissions()
{
	int i, rc = LDAP_SUCCESS;
	const char *text;

	for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
		rc = slap_bv2ad( &rbac_session_permission_ads[i].attr,
				rbac_session_permission_ads[i].ad, &text );
		if ( rc != LDAP_SUCCESS ) {
			goto done;
		}
	}

done:;
	return rc;
}

int
rbac_initialize_repository()
{
	int rc = LDAP_SUCCESS;

	rc = initialize_jts();
	if ( rc != LDAP_SUCCESS ) {
		return rc;
	}

	rc = initialize_rbac_session_permissions();
	if ( rc != LDAP_SUCCESS ) {
		return rc;
	}

	rc = initialize_sessions();
	if ( rc != LDAP_SUCCESS ) {
		return rc;
	}

	rc = initialize_audit();
	if ( rc != LDAP_SUCCESS ) {
		return rc;
	}

	return rc;
}
