Skip to content

Commit dd23a06

Browse files
Merge pull request #70 from ambitus/feat/ras_security_updates
RAS Update
2 parents 8d96ac1 + bebedbb commit dd23a06

File tree

81 files changed

+2495
-556
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2495
-556
lines changed

Jenkinsfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def clean_python_environment() {
175175
def install_poetry(python) {
176176
echo "Installing Poetry..."
177177

178-
sh "bash -c 'curl -sSL https://install.python-poetry.org | ${python} -'"
178+
sh "bash -c 'curl -sSL https://install.python-poetry.org' | ${python} -"
179179
}
180180

181181
def build_poetry_environment(python) {

pyproject.toml

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[build-system]
22
build-backend = "poetry.core.masonry.api"
3-
requires = ["poetry-core>=1.7.0", "setuptools>=61"]
3+
requires = ["poetry-core>=1.9.0", "setuptools>=69"]
44

55
[tool.poetry]
66
name="pyracf"
7-
version="1.0b4"
7+
version="1.0b5"
88
description="Python interface to RACF using IRRSMO00 RACF Callable Service."
99
license = "Apache-2.0"
1010
authors = [
@@ -44,13 +44,14 @@
4444
defusedxml = ">=0.7.1"
4545

4646
[tool.poetry.group.dev.dependencies]
47-
isort = ">=5.12.0"
48-
pre-commit = ">=3.4.0"
49-
black = ">=23.9.1"
50-
flake8 = ">=6.1.0"
51-
pylint = ">=3.0.0"
52-
coverage = ">=7.3.2"
53-
wheel = ">=0.41.2"
47+
isort = ">=5.13.2"
48+
pre-commit = ">=3.6.0"
49+
black = ">=24.1.1"
50+
flake8 = ">=7.0.0"
51+
pylint = ">=3.0.3"
52+
coverage = ">=7.4.1"
53+
wheel = ">=0.42.0"
54+
ebcdic = ">=1.1.1"
5455

5556
[tool.isort]
5657
profile = "black"

pyracf/__init__.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""Make security admin subclasses available from package root."""
2+
23
from .access.access_admin import AccessAdmin
3-
from .common.add_operation_error import AddOperationError
4-
from .common.alter_operation_error import AlterOperationError
5-
from .common.downstream_fatal_error import DownstreamFatalError
6-
from .common.security_request_error import SecurityRequestError
7-
from .common.segment_error import SegmentError
8-
from .common.segment_trait_error import SegmentTraitError
9-
from .common.userid_error import UserIdError
4+
from .common.exceptions.add_operation_error import AddOperationError
5+
from .common.exceptions.alter_operation_error import AlterOperationError
6+
from .common.exceptions.downstream_fatal_error import DownstreamFatalError
7+
from .common.exceptions.security_request_error import SecurityRequestError
8+
from .common.exceptions.segment_error import SegmentError
9+
from .common.exceptions.segment_trait_error import SegmentTraitError
10+
from .common.exceptions.userid_error import UserIdError
1011
from .connection.connection_admin import ConnectionAdmin
1112
from .data_set.data_set_admin import DataSetAdmin
1213
from .group.group_admin import GroupAdmin

pyracf/access/access_admin.py

+4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class AccessAdmin(SecurityAdmin):
1212

1313
def __init__(
1414
self,
15+
irrsmo00_result_buffer_size: Union[int, None] = None,
1516
debug: bool = False,
17+
dump_mode: bool = False,
1618
generate_requests_only: bool = False,
1719
update_existing_segment_traits: Union[dict, None] = None,
1820
replace_existing_segment_traits: Union[dict, None] = None,
@@ -44,7 +46,9 @@ def __init__(
4446
}
4547
super().__init__(
4648
"permission",
49+
irrsmo00_result_buffer_size=irrsmo00_result_buffer_size,
4750
debug=debug,
51+
dump_mode=dump_mode,
4852
generate_requests_only=generate_requests_only,
4953
update_existing_segment_traits=update_existing_segment_traits,
5054
replace_existing_segment_traits=replace_existing_segment_traits,

pyracf/common/downstream_fatal_error.py renamed to pyracf/common/exceptions/downstream_fatal_error.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Exception to use when IRRSMO00 is unable to process a request."""
2+
23
from typing import Union
34

45

pyracf/common/irrsmo00.c

+130-76
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,161 @@
1+
#define PY_SSIZE_T_CLEAN
12
#include <Python.h>
23

34
#include <unistd.h>
45
#include <signal.h>
56
#include <stdio.h>
67
#include <string.h>
78

8-
#define BUFFER_SIZE (100000)
9-
109
#pragma linkage(IRRSMO64, OS)
1110

12-
typedef struct {
13-
unsigned char len;
14-
char str[8];
15-
} VarStr_T;
16-
17-
// This function changes any null character not preceded by '>' to a blank character.
18-
// This is a workaround for an issue where profile data embedded in response xml
19-
// returned by IRROSMO00 sometimes includes null characters instead of properly
20-
// encoded text, which causes the returned xml to be truncated.
21-
void null_byte_fix(char* str, unsigned int str_len) {
22-
for (int i = 1; i < str_len; i++){
23-
if (str[i] == 0) {
24-
if (str[i-1] == 0x6E) {
25-
return;
26-
}
27-
else {
28-
str[i] = 0x40;
29-
}
30-
}
31-
}
32-
}
33-
34-
static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) {
35-
const unsigned int xml_len;
36-
const unsigned int input_opts;
37-
const uint8_t input_userid_len;
38-
const char *input_xml;
39-
const char *input_userid;
40-
41-
static char *kwlist[] = {"xml_str", "xml_len", "opts", "userid", "userid_len", NULL};
11+
typedef struct
12+
{
13+
unsigned char running_userid_length;
14+
char running_userid[8];
15+
} running_userid_t;
4216

43-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y|IIyb", kwlist, &input_xml, &xml_len, &input_opts, &input_userid, &input_userid_len)) {
17+
static PyObject *call_irrsmo00(PyObject *self, PyObject *args, PyObject *kwargs)
18+
{
19+
const char *request_xml;
20+
const unsigned int request_xml_length;
21+
const unsigned int result_buffer_size;
22+
const unsigned int irrsmo00_options;
23+
const char *running_userid;
24+
const uint8_t running_userid_length;
25+
26+
static char *kwlist[] = {
27+
"request_xml",
28+
"request_xml_length",
29+
"result_buffer_size",
30+
"irrsmo00_options",
31+
"running_userid",
32+
"running_userid_length",
33+
NULL};
34+
35+
if (
36+
!PyArg_ParseTupleAndKeywords(
37+
args,
38+
kwargs,
39+
"y|IIIyb",
40+
kwlist,
41+
&request_xml,
42+
&request_xml_length,
43+
&result_buffer_size,
44+
&irrsmo00_options,
45+
&running_userid,
46+
&running_userid_length))
47+
{
4448
return NULL;
4549
}
4650

47-
4851
char work_area[1024];
49-
char req_handle[64] = { 0 };
50-
VarStr_T userid = { input_userid_len, {0}};
52+
char req_handle[64] = {0};
53+
running_userid_t running_userid_struct = {running_userid_length, {0}};
5154
unsigned int alet = 0;
5255
unsigned int acee = 0;
53-
unsigned char rsp[BUFFER_SIZE+1];
54-
memset(rsp, 0, BUFFER_SIZE);
55-
unsigned int saf_rc=0, racf_rc=0, racf_rsn=0;
56-
unsigned int num_parms=17, fn=1, opts = input_opts, rsp_len = sizeof(rsp)-1;
57-
58-
strncpy(userid.str, input_userid, userid.len);
56+
unsigned char result_buffer[result_buffer_size];
57+
memset(result_buffer, 0, result_buffer_size);
58+
unsigned int saf_rc = 0;
59+
unsigned int racf_rc = 0;
60+
unsigned int racf_rsn = 0;
61+
unsigned int num_parms = 17;
62+
unsigned int fn = 1;
63+
64+
strncpy(
65+
running_userid_struct.running_userid,
66+
running_userid,
67+
running_userid_struct.running_userid_length);
5968

6069
IRRSMO64(
61-
work_area,
62-
alet,
63-
&saf_rc,
64-
alet,
65-
&racf_rc,
66-
alet,
67-
&racf_rsn,
68-
num_parms,
69-
fn,
70-
opts,
71-
xml_len,
72-
input_xml,
73-
req_handle,
74-
userid,
75-
acee,
76-
rsp_len,
77-
rsp
78-
);
79-
80-
null_byte_fix(rsp,rsp_len);
81-
82-
return Py_BuildValue("yBBB", rsp, saf_rc, racf_rc, racf_rsn);
70+
work_area,
71+
alet,
72+
&saf_rc,
73+
alet,
74+
&racf_rc,
75+
alet,
76+
&racf_rsn,
77+
num_parms,
78+
fn,
79+
irrsmo00_options,
80+
request_xml_length,
81+
request_xml,
82+
req_handle,
83+
running_userid_struct,
84+
acee,
85+
result_buffer_size,
86+
result_buffer);
87+
88+
// https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
89+
//
90+
// According to the Python 3 C API documentation:
91+
// When memory buffers are passed as parameters to supply data to
92+
// build objects, as for the s and s# formats, the required data is
93+
// copied. Buffers provided by the caller are never referenced by
94+
// the objects created by Py_BuildValue(). In other words, if your
95+
// code invokes malloc() and passes the allocated memory to
96+
// Py_BuildValue(), your code is responsible for calling free() for
97+
// that memory once Py_BuildValue() returns.
98+
//
99+
// y# (bytes) [const char *, Py_ssize_t]
100+
// This converts a C string and its lengths to a Python object.
101+
// If the C string pointer is NULL, None is returned.
102+
//
103+
// https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
104+
//
105+
// So, given that 'result_buffer' is a stack allocated buffer
106+
// and that Python creates a copy of the buffer, which Python's
107+
// garbage collection should be responsible for, we do not need
108+
// to do any memory mangement here. 'result_buffer' will simply
109+
// just be popped off the stack when this function returns.
110+
//
111+
// Also, according to the Python3 C API documentation, 'y#' should
112+
// be just giving us a copy copy of exactly what is in the buffer,
113+
// without attempting to do any transformations to the data.
114+
// The following GeesForGeeks article futher confirms that we are
115+
// going to get a bytes object that is completely unmanipulated.
116+
// https://www.geeksforgeeks.org/c-strings-conversion-to-python/
117+
//
118+
// In this case, all post processing of the data is handled on
119+
// the Python side.
120+
//
121+
// Also note that when two or more return values are provided,
122+
// Py_BuildValue() will return a Tuple.
123+
124+
return Py_BuildValue(
125+
"y#BBB",
126+
result_buffer,
127+
result_buffer_size,
128+
saf_rc,
129+
racf_rc,
130+
racf_rsn);
83131
}
84132

85133
static char call_irrsmo00_docs[] =
86-
"call_irrsmo00(input_xml: bytes, xml_len: uint, opts: uint): Returns an XML response string and return and reason codes from the IRRSMO00 RACF Callable Service.\n";
134+
"call_irrsmo00(\n"
135+
" request_xml: bytes,\n"
136+
" request_xml_length: uint,\n"
137+
" result_buffer_size: uint,\n"
138+
" irrsmo00_options: uint,\n"
139+
" running_userid: bytes,\n"
140+
" running_userid_length: uint,\n"
141+
") -> List[bytes,int,int,int]:\n"
142+
"# Returns an XML result string and return and reason "
143+
"codes from the IRRSMO00 RACF Callable Service.\n";
87144

88145
static PyMethodDef cpyracf_methods[] = {
89-
{"call_irrsmo00", (PyCFunction)call_irrsmo00,
90-
METH_VARARGS | METH_KEYWORDS, call_irrsmo00_docs},
91-
{NULL}
92-
};
146+
{"call_irrsmo00", (PyCFunction)call_irrsmo00,
147+
METH_VARARGS | METH_KEYWORDS, call_irrsmo00_docs},
148+
{NULL}};
93149

94-
static struct PyModuleDef cpyracf_module_def =
95-
{
150+
static struct PyModuleDef cpyracf_module_def = {
96151
PyModuleDef_HEAD_INIT,
97-
"cpyracf",
152+
"cpyracf",
98153
"C code that enables pyRACF to call the IRRSMO00 RACF callable service.\n",
99154
-1,
100-
cpyracf_methods
101-
};
155+
cpyracf_methods};
102156

103157
PyMODINIT_FUNC PyInit_cpyracf(void)
104158
{
105-
Py_Initialize();
106-
return PyModule_Create(&cpyracf_module_def);
159+
Py_Initialize();
160+
return PyModule_Create(&cpyracf_module_def);
107161
}

0 commit comments

Comments
 (0)