From: Kamen Mazdrashki Date: Mon, 29 Mar 2010 09:31:38 +0000 (+0300) Subject: s4/tort: Unit test for schemaInfo implementation X-Git-Url: http://git.samba.org/?a=commitdiff_plain;h=526e62c46f432950645dc7019969eb694d7b0c6b;p=kamenim%2Fsamba.git s4/tort: Unit test for schemaInfo implementation --- diff --git a/source4/torture/drs/config.mk b/source4/torture/drs/config.mk index 0ce3a3d17a..0fdeb97df1 100644 --- a/source4/torture/drs/config.mk +++ b/source4/torture/drs/config.mk @@ -19,6 +19,7 @@ PRIVATE_DEPENDENCIES = \ TORTURE_DRS_OBJ_FILES = \ $(torturesrcdir)/drs/drs_init.o \ $(torturesrcdir)/drs/drs_util.o \ + $(torturesrcdir)/drs/unit/schemainfo_tests.o \ $(torturesrcdir)/drs/unit/prefixmap_tests.o $(eval $(call proto_header_template,$(torturesrcdir)/drs/proto.h,$(TORTURE_DRS_OBJ_FILES:.o=.c))) diff --git a/source4/torture/drs/drs_init.c b/source4/torture/drs/drs_init.c index 081565fc57..35520ba54a 100644 --- a/source4/torture/drs/drs_init.c +++ b/source4/torture/drs/drs_init.c @@ -22,6 +22,7 @@ #include "includes.h" #include "torture/smbtorture.h" #include "torture/rpc/drsuapi.h" +#include "dsdb/samdb/samdb.h" #include "torture/drs/proto.h" /** @@ -46,6 +47,7 @@ static struct torture_suite * torture_drs_unit_suite(TALLOC_CTX *mem_ctx) "UNIT"); torture_drs_unit_prefixmap(suite); + torture_drs_unit_schemainfo(suite); return suite; } diff --git a/source4/torture/drs/unit/schemainfo_tests.c b/source4/torture/drs/unit/schemainfo_tests.c new file mode 100644 index 0000000000..ab9de87f22 --- /dev/null +++ b/source4/torture/drs/unit/schemainfo_tests.c @@ -0,0 +1,579 @@ +/* + Unix SMB/CIFS implementation. + + DRSUAPI schemaInfo unit tests + + Copyright (C) Kamen Mazdrashki 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "torture/smbtorture.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/samdb/ldb_modules/util.h" +#include "lib/ldb_wrap.h" +#include "lib/ldb/include/ldb_module.h" +#include "torture/rpc/drsuapi.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" + + +/** + * schemaInfo to init ldb context with + * Rev: 01 + * GUID: 071c82fd-45c7-4351-a3db-51f75a630a7f + */ +#define SCHEMA_INFO_INIT_STR "FF0000000100000000000000000000000000000000" + +/** + * Default schema_info string to be used for testing + * Rev: 01 + * GUID: 071c82fd-45c7-4351-a3db-51f75a630a7f + */ +#define SCHEMA_INFO_DEFAULT_STR "FF00000001FD821C07C7455143A3DB51F75A630A7F" + +/** + * Schema info data to test with + */ +struct schemainfo_data { + DATA_BLOB ndr_blob; + struct dsdb_schema_info schi; + WERROR werr_expected; + bool test_both_ways; +}; + +/** + * Schema info test data in human-readable format (... kind of) + */ +static const struct { + const char *schema_info_str; + uint32_t revision; + const char *guid_str; + WERROR werr_expected; + bool test_both_ways; +} _schemainfo_test_data[] = { + { + .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_OK, + .test_both_ways = true + }, + { + .schema_info_str = "FFFFFFFFFFFD821C07C7455143A3DB51F75A630A7F", + .revision = 0xFFFFFFFF, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_OK, + .test_both_ways = true + }, + { /* revision > 0 */ + .schema_info_str = "FF00000000FD821C07C7455143A3DB51F75A630A7F", + .revision = 0, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_INVALID_PARAMETER, + .test_both_ways = true + }, + { /* len == 21 */ + .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F00", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_INVALID_PARAMETER, + .test_both_ways = false + }, + { /* marker == FF */ + .schema_info_str = "AA00000001FD821C07C7455143A3DB51F75A630A7F", + .revision = 1, + .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f", + .werr_expected = WERR_INVALID_PARAMETER, + .test_both_ways = false + } +}; + +/** + * Private data to be shared among all test in Test case + */ +struct drsut_schemainfo_data { + struct ldb_context *ldb; + struct ldb_module *ldb_module; + struct dsdb_schema *schema; + + /* Initial schemaInfo set in ldb to test with */ + struct dsdb_schema_info *schema_info; + + uint32_t test_data_count; + struct schemainfo_data *test_data; +}; + +/** + * torture macro to assert for equal dsdb_schema_info's + */ +#define torture_assert_schema_info_equal(torture_ctx,got,expected,cmt)\ + do { const struct dsdb_schema_info *__got = (got), *__expected = (expected); \ + if (__got->revision != __expected->revision) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got".revision %d did not match "#expected".revision %d: %s", \ + (int)__got->revision, (int)__expected->revision, cmt); \ + return false; \ + } \ + if (!GUID_equal(&__got->invocation_id, &__expected->invocation_id)) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got".invocation_id did not match "#expected".invocation_id: %s", cmt); \ + return false; \ + } \ + } while(0) + +/* + * forward declaration for internal functions + */ +static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx, + struct ldb_context *ldb, + const char *schema_info_str, + bool in_setup); + + +/** + * Creates dsdb_schema_info object based on NDR data + * passed as hex string + */ +static bool _drsut_schemainfo_new(struct torture_context *tctx, + const char *schema_info_str, struct dsdb_schema_info **_si) +{ + WERROR werr; + DATA_BLOB blob; + + blob = strhex_to_data_blob(tctx, schema_info_str); + if (!blob.data) { + torture_comment(tctx, "Not enough memory!\n"); + return false; + } + + werr = dsdb_schema_info_from_blob(&blob, tctx, _si); + if (!W_ERROR_IS_OK(werr)) { + torture_comment(tctx, + "Failed to create dsdb_schema_info object for %s: %s", + schema_info_str, + win_errstr(werr)); + return false; + } + + data_blob_free(&blob); + + return true; +} + +/** + * Creates dsdb_schema_info object based on predefined data + * Function is public as it is intended to be used by other + * tests (e.g. prefixMap tests) + */ +bool drsut_schemainfo_new(struct torture_context *tctx, struct dsdb_schema_info **_si) +{ + return _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, _si); +} + + +/* + * Tests dsdb_schema_info_from_blob() + */ +static bool test_dsdb_schema_info_from_blob(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int i; + WERROR werr; + char *msg; + struct dsdb_schema_info *schema_info; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + for (i = 0; i < priv->test_data_count; i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + msg = talloc_asprintf(tctx, "dsdb_schema_info_from_blob() [%d]-[%s]", + i, _schemainfo_test_data[i].schema_info_str); + + werr = dsdb_schema_info_from_blob(&data->ndr_blob, mem_ctx, &schema_info); + torture_assert_werr_equal(tctx, werr, data->werr_expected, msg); + + /* test returned data */ + if (W_ERROR_IS_OK(werr)) { + torture_assert_schema_info_equal(tctx, + schema_info, &data->schi, + "after dsdb_schema_info_from_blob() call"); + } + } + + talloc_free(mem_ctx); + + return true; +} + +/* + * Tests dsdb_blob_from_schema_info() + */ +static bool test_dsdb_blob_from_schema_info(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int i; + WERROR werr; + char *msg; + DATA_BLOB ndr_blob; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + for (i = 0; i < priv->test_data_count; i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + /* not all test are valid reverse type of conversion */ + if (!data->test_both_ways) { + continue; + } + + msg = talloc_asprintf(tctx, "dsdb_blob_from_schema_info() [%d]-[%s]", + i, _schemainfo_test_data[i].schema_info_str); + + werr = dsdb_blob_from_schema_info(&data->schi, mem_ctx, &ndr_blob); + torture_assert_werr_equal(tctx, werr, data->werr_expected, msg); + + /* test returned data */ + if (W_ERROR_IS_OK(werr)) { + torture_assert_data_blob_equal(tctx, + ndr_blob, data->ndr_blob, + "dsdb_blob_from_schema_info()"); + } + } + + talloc_free(mem_ctx); + + return true; +} + +/* + * Tests dsdb_module_schema_info_blob_read() + * and dsdb_module_schema_info_blob_write() + */ +static bool test_dsdb_module_schema_info_blob_rw(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + WERROR werr; + DATA_BLOB blob_write; + DATA_BLOB blob_read; + + /* reset schmeInfo to know value */ + torture_assert(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false), + "_drsut_ldb_schema_info_reset() failed"); + + /* write tests' default schemaInfo */ + blob_write = strhex_to_data_blob(priv, SCHEMA_INFO_DEFAULT_STR); + torture_assert(tctx, blob_write.data, "Not enough memory!"); + + werr = dsdb_module_schema_info_blob_write(priv->ldb_module, + DSDB_FLAG_TOP_MODULE, + &blob_write); + torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_write() failed"); + + werr = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE, + priv, &blob_read); + torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_read() failed"); + + /* check if we get what we wrote */ + torture_assert_data_blob_equal(tctx, blob_read, blob_write, + "Write/Read of schemeInfo blob failed"); + + return true; +} + +/* + * Tests dsdb_schema_update_schema_info() + */ +static bool test_dsdb_module_schema_info_update(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + WERROR werr; + DATA_BLOB blob; + struct dsdb_schema_info *schema_info; + + /* reset schmeInfo to know value */ + torture_assert(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false), + "_drsut_ldb_schema_info_reset() failed"); + + werr = dsdb_module_schema_info_update(priv->ldb_module, + priv->schema, + DSDB_FLAG_TOP_MODULE | DSDB_FLAG_AS_SYSTEM); + torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_update() failed"); + + /* get updated schemaInfo */ + werr = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE, + priv, &blob); + torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_read() failed"); + + werr = dsdb_schema_info_from_blob(&blob, priv, &schema_info); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_from_blob() failed"); + + /* decrement revision to be able to compare + * against default schemaInfo later */ + schema_info->revision--; + + /* check against default schema_info */ + torture_assert_schema_info_equal(tctx, schema_info, priv->schema_info, + "schemaInfo attribute no updated correctly"); + + return true; +} + +/* + * Tests dsdb_schema_info_create() + */ +static bool test_dsdb_schema_info_create(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + WERROR werr; + struct dsdb_schema_info *schema_info = NULL; + + werr = dsdb_schema_info_create(priv->ldb, priv, &schema_info); + torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_create() failed"); + + torture_assert(tctx, schema_info, "schema_info is NULL after dsdb_schema_info_create()"); + torture_assert_int_equal(tctx, schema_info->revision, 1, "Invalid schemaInfo revision"); + torture_assert(tctx, + GUID_equal(&schema_info->invocation_id, &priv->schema_info->invocation_id), + "Invalid invocationId returned"); + + talloc_free(schema_info); + + return true; +} + + +/** + * Reset schemaInfo record to know value + */ +static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx, + struct ldb_context *ldb, + const char *schema_info_str, + bool in_setup) +{ + bool bret = true; + int ldb_err; + DATA_BLOB blob; + struct ldb_message *msg; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + blob = strhex_to_data_blob(mem_ctx, schema_info_str); + torture_assert_goto(tctx, blob.data, bret, DONE, "Not enough memory!"); + + msg = ldb_msg_new(mem_ctx); + torture_assert_goto(tctx, msg, bret, DONE, "Not enough memory!"); + + msg->dn = samdb_schema_dn(ldb); + ldb_err = ldb_msg_add_value(msg, "schemaInfo", &blob, NULL); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "ldb_msg_add_value() failed"); + + if (in_setup) { + ldb_err = ldb_add(ldb, msg); + } else { + ldb_err = dsdb_replace(ldb, msg, DSDB_MODIFY_PERMISSIVE); + } + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "dsdb_replace() failed"); + +DONE: + talloc_free(mem_ctx); + return bret; +} + +/** + * Prepare temporary LDB and opens it + */ +static bool _drsut_ldb_setup(struct torture_context *tctx, struct drsut_schemainfo_data *priv) +{ + int ldb_err; + char *ldb_url; + bool bret = true; + char *tempdir = NULL; + NTSTATUS status; + TALLOC_CTX* mem_ctx; + + mem_ctx = talloc_new(priv); + torture_assert(tctx, mem_ctx, "Not enough memory!"); + + status = torture_temp_dir(tctx, "drs_", &tempdir); + torture_assert_ntstatus_ok_goto(tctx, status, bret, DONE, "creating temp dir"); + + ldb_url = talloc_asprintf(priv, "%s/drs_schemainfo.ldb", tempdir); + torture_assert_goto(tctx, ldb_url, bret, DONE, "Not enough memory!"); + + /* create LDB */ + priv->ldb = ldb_wrap_connect(priv, tctx->ev, tctx->lp_ctx, + ldb_url, NULL, NULL, 0); + torture_assert_goto(tctx, priv->ldb, bret, DONE, "ldb_wrap_connect() failed"); + + /* set some schemaNamingContext */ + ldb_err = ldb_set_opaque(priv->ldb, + "schemaNamingContext", + ldb_dn_new(priv->ldb, priv->ldb, "CN=Schema,CN=Config")); + torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE, + "ldb_set_opaque() failed"); + + /* add schemaInfo attribute so tested layer could work properly */ + torture_assert_goto(tctx, + _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, true), + bret, DONE, + "_drsut_ldb_schema_info_reset() failed"); + +DONE: + talloc_free(tempdir); + talloc_free(mem_ctx); + return bret; +} + +/* + * Setup/Teardown for test case + */ +static bool torture_drs_unit_schemainfo_setup(struct torture_context *tctx, + struct drsut_schemainfo_data **_priv) +{ + int i; + int ldb_err; + NTSTATUS status; + DATA_BLOB ndr_blob; + struct GUID guid; + struct drsut_schemainfo_data *priv; + + priv = talloc_zero(tctx, struct drsut_schemainfo_data); + torture_assert(tctx, priv, "Not enough memory!"); + + /* returned allocated pointer here + * teardown() will be called even in case of failure, + * so we'll get a changes to clean up */ + *_priv = priv; + + /* create initial schemaInfo */ + torture_assert(tctx, + _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, &priv->schema_info), + "Failed to create schema_info test object"); + + /* create data to test with */ + priv->test_data_count = ARRAY_SIZE(_schemainfo_test_data); + priv->test_data = talloc_array(tctx, struct schemainfo_data, priv->test_data_count); + + for (i = 0; i < ARRAY_SIZE(_schemainfo_test_data); i++) { + struct schemainfo_data *data = &priv->test_data[i]; + + ndr_blob = strhex_to_data_blob(priv, + _schemainfo_test_data[i].schema_info_str); + torture_assert(tctx, ndr_blob.data, "Not enough memory!"); + + status = GUID_from_string(_schemainfo_test_data[i].guid_str, &guid); + torture_assert_ntstatus_ok(tctx, status, + talloc_asprintf(tctx, + "GUID_from_string() failed for %s", + _schemainfo_test_data[i].guid_str)); + + data->ndr_blob = ndr_blob; + data->schi.invocation_id = guid; + data->schi.revision = _schemainfo_test_data[i].revision; + data->werr_expected = _schemainfo_test_data[i].werr_expected; + data->test_both_ways = _schemainfo_test_data[i].test_both_ways; + + } + + /* create temporary LDB and populate with data */ + if (!_drsut_ldb_setup(tctx, priv)) { + return false; + } + + /* create ldb_module mockup object */ + priv->ldb_module = ldb_module_new(priv, priv->ldb, "schemaInfo_test_module", NULL); + torture_assert(tctx, priv->ldb_module, "Not enough memory!"); + + /* create schema mockup object */ + priv->schema = dsdb_new_schema(priv, lp_iconv_convenience(tctx->lp_ctx)); + + /* pre-cache invocationId for samdb_ntds_invocation_id() + * to work with our mock ldb */ + ldb_err = ldb_set_opaque(priv->ldb, "cache.invocation_id", + &priv->schema_info->invocation_id); + torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "ldb_set_opaque() failed"); + + /* Perform all tests in transactions so that + * underlying modify calls not to fail */ + ldb_err = ldb_transaction_start(priv->ldb); + torture_assert_int_equal(tctx, + ldb_err, + LDB_SUCCESS, + "ldb_transaction_start() failed"); + + return true; +} + +static bool torture_drs_unit_schemainfo_teardown(struct torture_context *tctx, + struct drsut_schemainfo_data *priv) +{ + int ldb_err; + + /* commit pending transaction so we will + * be able to check what LDB state is */ + ldb_err = ldb_transaction_commit(priv->ldb); + if (ldb_err != LDB_SUCCESS) { + torture_comment(tctx, "ldb_transaction_commit() - %s (%s)", + ldb_strerror(ldb_err), + ldb_errstring(priv->ldb)); + } + + talloc_free(priv); + + return true; +} + +/** + * Test case initialization for + * DRS-UNIT.schemaInfo + */ +struct torture_tcase * torture_drs_unit_schemainfo(struct torture_suite *suite) +{ + typedef bool (*pfn_setup)(struct torture_context *, void **); + typedef bool (*pfn_teardown)(struct torture_context *, void *); + typedef bool (*pfn_run)(struct torture_context *, void *); + + struct torture_tcase * tc = torture_suite_add_tcase(suite, "schemaInfo"); + + torture_tcase_set_fixture(tc, + (pfn_setup)torture_drs_unit_schemainfo_setup, + (pfn_teardown)torture_drs_unit_schemainfo_teardown); + + tc->description = talloc_strdup(tc, "Unit tests for DRSUAPI::schemaInfo implementation"); + + torture_tcase_add_simple_test(tc, "dsdb_schema_info_from_blob", + (pfn_run)test_dsdb_schema_info_from_blob); + torture_tcase_add_simple_test(tc, "dsdb_blob_from_schema_info", + (pfn_run)test_dsdb_blob_from_schema_info); + torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_blob read|write", + (pfn_run)test_dsdb_module_schema_info_blob_rw); + torture_tcase_add_simple_test(tc, "dsdb_schema_info_create", + (pfn_run)test_dsdb_schema_info_create); + torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_update", + (pfn_run)test_dsdb_module_schema_info_update); + + + return tc; +}