Commit b98eb3e1 authored by Vitaly Lipatov's avatar Vitaly Lipatov

remove vendor dir

parent 8ee9666b
......@@ -100,8 +100,6 @@ class SettingsdInstall(install) :
const_py_file.close()
except : pass
shutil.copytree("vendor/file_read_backwards", os.path.join(self.install_libbase, 'file_read_backwards'))
##### Main #####
setup(
......
===============================
file_read_backwards
===============================
.. image:: https://img.shields.io/pypi/v/file_read_backwards.svg
:target: https://pypi.python.org/pypi/file_read_backwards
.. image:: https://img.shields.io/travis/RobinNil/file_read_backwards.svg?branch=master
:target: https://travis-ci.org/RobinNil/file_read_backwards.svg?branch=master
.. image:: https://readthedocs.org/projects/file-read-backwards/badge/?version=latest
:target: https://file-read-backwards.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://pyup.io/repos/github/RobinNil/file_read_backwards/shield.svg
:target: https://pyup.io/repos/github/RobinNil/file_read_backwards/
:alt: Updates
Memory efficient way of reading files line-by-line from the end of file
* Free software: MIT license
* Documentation: https://file-read-backwards.readthedocs.io.
Features
--------
This package is for reading file backward line by line as unicode in a memory efficient manner for both Python 2.7 and Python 3.
It currently supports ascii, latin-1, and utf-8 encodings.
It supports "\\r", "\\r\\n", and "\\n" as new lines.
Usage Examples
--------------
An example of using `file_read_backwards` for `python2.7`::
#!/usr/bin/env python2.7
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
# getting lines by lines starting from the last line up
for l in frb:
print l
Another example using `python3.3`::
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
# getting lines by lines starting from the last line up
for l in frb:
print(l)
Another way to consume the file is via `readline()`, in `python3.3`::
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
while True:
l = frb.readline()
if not l:
break
print(l, end="")
Credits
---------
This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage
=======
History
=======
1.0.0 (2016-12-18)
------------------
* First release on PyPI.
1.1.0 (2016-12-31)
------------------
* Added support for "latin-1".
* Marked the package "Production/Stable".
1.1.1 (2017-01-09)
------------------
* Updated README.rst for more clarity around encoding support and Python 2.7 and 3 support.
1.1.2 (2017-01-11)
------------------
* Documentation re-arrangement. Usage examples are now in README.rst
* Minor refactoring
1.2.0 (2017-09-01)
------------------
* Include context manager style as it provides cleaner/automatic close functionality
1.2.1 (2017-09-02)
------------------
* Made doc strings consistent to Google style and some code linting
1.2.2 (2017-11-19)
------------------
* Re-release of 1.2.1 for ease of updating pypi page for updated travis & pyup.
2.0.0 (2018-03-23)
------------------
Mimicing Python file object behavior.
* FileReadBackwards no longer creates multiple iterators (a change of behavior from 1.x.y version)
* Adding readline() function retuns one line at a time with a trailing new line and empty string when it reaches end of file.
The fine print: the trailing new line will be `os.linesep` (rather than whichever new line type in the file).
Metadata-Version: 2.0
Name: file-read-backwards
Version: 2.0.0
Summary: Memory efficient way of reading files line-by-line from the end of file
Home-page: https://github.com/RobinNil/file_read_backwards
Author: Robin Robin
Author-email: robinsquare42@gmail.com
License: MIT license
Keywords: file_read_backwards
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
===============================
file_read_backwards
===============================
.. image:: https://img.shields.io/pypi/v/file_read_backwards.svg
:target: https://pypi.python.org/pypi/file_read_backwards
.. image:: https://img.shields.io/travis/RobinNil/file_read_backwards.svg?branch=master
:target: https://travis-ci.org/RobinNil/file_read_backwards.svg?branch=master
.. image:: https://readthedocs.org/projects/file-read-backwards/badge/?version=latest
:target: https://file-read-backwards.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://pyup.io/repos/github/RobinNil/file_read_backwards/shield.svg
:target: https://pyup.io/repos/github/RobinNil/file_read_backwards/
:alt: Updates
Memory efficient way of reading files line-by-line from the end of file
* Free software: MIT license
* Documentation: https://file-read-backwards.readthedocs.io.
Features
--------
This package is for reading file backward line by line as unicode in a memory efficient manner for both Python 2.7 and Python 3.
It currently supports ascii, latin-1, and utf-8 encodings.
It supports "\\r", "\\r\\n", and "\\n" as new lines.
Usage Examples
--------------
An example of using `file_read_backwards` for `python2.7`::
#!/usr/bin/env python2.7
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
# getting lines by lines starting from the last line up
for l in frb:
print l
Another example using `python3.3`::
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
# getting lines by lines starting from the last line up
for l in frb:
print(l)
Another way to consume the file is via `readline()`, in `python3.3`::
from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
while True:
l = frb.readline()
if not l:
break
print(l, end="")
Credits
---------
This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage
=======
History
=======
1.0.0 (2016-12-18)
------------------
* First release on PyPI.
1.1.0 (2016-12-31)
------------------
* Added support for "latin-1".
* Marked the package "Production/Stable".
1.1.1 (2017-01-09)
------------------
* Updated README.rst for more clarity around encoding support and Python 2.7 and 3 support.
1.1.2 (2017-01-11)
------------------
* Documentation re-arrangement. Usage examples are now in README.rst
* Minor refactoring
1.2.0 (2017-09-01)
------------------
* Include context manager style as it provides cleaner/automatic close functionality
1.2.1 (2017-09-02)
------------------
* Made doc strings consistent to Google style and some code linting
1.2.2 (2017-11-19)
------------------
* Re-release of 1.2.1 for ease of updating pypi page for updated travis & pyup.
2.0.0 (2018-03-23)
------------------
Mimicing Python file object behavior.
* FileReadBackwards no longer creates multiple iterators (a change of behavior from 1.x.y version)
* Adding readline() function retuns one line at a time with a trailing new line and empty string when it reaches end of file.
The fine print: the trailing new line will be `os.linesep` (rather than whichever new line type in the file).
file_read_backwards-2.0.0.dist-info/DESCRIPTION.rst,sha256=UXNL9zcu_H5XjeCfnxqhADk3kQg5WS8qx8_GkyKDnv0,3647
file_read_backwards-2.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
file_read_backwards-2.0.0.dist-info/METADATA,sha256=SNqkrzocPrhWxro5NSVc6IVjEuy_ivz9UkP5XoTAQ_w,4417
file_read_backwards-2.0.0.dist-info/RECORD,,
file_read_backwards-2.0.0.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
file_read_backwards-2.0.0.dist-info/metadata.json,sha256=J2rLVwakld4LYHi1CVSoZMAhxTAK-T5i0gbCDienb38,942
file_read_backwards-2.0.0.dist-info/top_level.txt,sha256=J0c-zzN9i4B3noENqqGllyULovoXYowT-_VsvP5obD8,20
file_read_backwards/__init__.py,sha256=EgTdw29vRAhhLjqLt6AIH-trsQOcv9w843hhm43x1tA,182
file_read_backwards/__pycache__/__init__.cpython-36.pyc,,
file_read_backwards/__pycache__/buffer_work_space.cpython-36.pyc,,
file_read_backwards/__pycache__/file_read_backwards.cpython-36.pyc,,
file_read_backwards/buffer_work_space.py,sha256=7OW2fFMeEB_HRamzOQigEabkFiCmLO50_byO9D1E6oM,6446
file_read_backwards/file_read_backwards.py,sha256=Gi-P6vNTWtlR9J_2o0OnWEsDkZdjaJcQGkxstvIICvA,4069
Wheel-Version: 1.0
Generator: bdist_wheel (0.30.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any
{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"], "extensions": {"python.details": {"contacts": [{"email": "robinsquare42@gmail.com", "name": "Robin Robin", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/RobinNil/file_read_backwards"}}}, "generator": "bdist_wheel (0.30.0)", "keywords": ["file_read_backwards"], "license": "MIT license", "metadata_version": "2.0", "name": "file-read-backwards", "summary": "Memory efficient way of reading files line-by-line from the end of file", "test_requires": [{"requires": ["mock"]}], "version": "2.0.0"}
\ No newline at end of file
# -*- coding: utf-8 -*-
from .file_read_backwards import FileReadBackwards # noqa: F401
__author__ = """Robin Robin"""
__email__ = 'robinsquare42@gmail.com'
__version__ = '2.0.0'
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""BufferWorkSpace module."""
import os
new_lines = ["\r\n", "\n", "\r"]
new_lines_bytes = [n.encode("ascii") for n in new_lines] # we only support encodings that's backward compat with ascii
class BufferWorkSpace:
"""It is a helper module for FileReadBackwards."""
def __init__(self, fp, chunk_size):
"""Convention for the data.
When read_buffer is not None, it represents contents of the file from `read_position` onwards
that has not been processed/returned.
read_position represents the file pointer position that has been read into read_buffer
initialized to be just past the end of file.
"""
self.fp = fp
self.read_position = _get_file_size(self.fp) # set the previously read position to the
self.read_buffer = None
self.chunk_size = chunk_size
def add_to_buffer(self, content, read_position):
"""Add additional bytes content as read from the read_position.
Args:
content (bytes): data to be added to buffer working BufferWorkSpac.
read_position (int): where in the file pointer the data was read from.
"""
self.read_position = read_position
if self.read_buffer is None:
self.read_buffer = content
else:
self.read_buffer = content + self.read_buffer
def yieldable(self):
"""Return True if there is a line that the buffer can return, False otherwise."""
if self.read_buffer is None:
return False
t = _remove_trailing_new_line(self.read_buffer)
n = _find_furthest_new_line(t)
if n >= 0:
return True
# we have read in entire file and have some unprocessed lines
if self.read_position == 0 and self.read_buffer is not None:
return True
return False
def return_line(self):
"""Return a new line if it is available.
Precondition: self.yieldable() must be True
"""
assert(self.yieldable())
t = _remove_trailing_new_line(self.read_buffer)
i = _find_furthest_new_line(t)
if i >= 0:
l = i + 1
after_new_line = slice(l, None)
up_to_include_new_line = slice(0, l)
r = t[after_new_line]
self.read_buffer = t[up_to_include_new_line]
else: # the case where we have read in entire file and at the "last" line
r = t
self.read_buffer = None
return r
def read_until_yieldable(self):
"""Read in additional chunks until it is yieldable."""
while not self.yieldable():
read_content, read_position = _get_next_chunk(self.fp, self.read_position, self.chunk_size)
self.add_to_buffer(read_content, read_position)
def has_returned_every_line(self):
"""Return True if every single line in the file has been returned, False otherwise."""
if self.read_position == 0 and self.read_buffer is None:
return True
return False
def _get_file_size(fp):
return os.fstat(fp.fileno()).st_size
def _get_next_chunk(fp, previously_read_position, chunk_size):
"""Return next chunk of data that we would from the file pointer.
Args:
fp: file-like object
previously_read_position: file pointer position that we have read from
chunk_size: desired read chunk_size
Returns:
(bytestring, int): data that has been read in, the file pointer position where the data has been read from
"""
seek_position, read_size = _get_what_to_read_next(fp, previously_read_position, chunk_size)
fp.seek(seek_position)
read_content = fp.read(read_size)
read_position = seek_position
return read_content, read_position
def _get_what_to_read_next(fp, previously_read_position, chunk_size):
"""Return information on which file pointer position to read from and how many bytes.
Args:
fp
past_read_positon (int): The file pointer position that has been read previously
chunk_size(int): ideal io chunk_size
Returns:
(int, int): The next seek position, how many bytes to read next
"""
seek_position = max(previously_read_position - chunk_size, 0)
read_size = chunk_size
# examples: say, our new_lines are potentially "\r\n", "\n", "\r"
# find a reading point where it is not "\n", rewind further if necessary
# if we have "\r\n" and we read in "\n",
# the next iteration would treat "\r" as a different new line.
# Q: why don't I just check if it is b"\n", but use a function ?
# A: so that we can potentially expand this into generic sets of separators, later on.
while seek_position > 0:
fp.seek(seek_position)
if _is_partially_read_new_line(fp.read(1)):
seek_position -= 1
read_size += 1 # as we rewind further, let's make sure we read more to compensate
else:
break
# take care of special case when we are back to the beginnin of the file
read_size = min(previously_read_position - seek_position, read_size)
return seek_position, read_size
def _remove_trailing_new_line(l):
"""Remove a single instance of new line at the end of l if it exists.
Returns:
bytestring
"""
# replace only 1 instance of newline
# match longest line first (hence the reverse=True), we want to match "\r\n" rather than "\n" if we can
for n in sorted(new_lines_bytes, key=lambda x: len(x), reverse=True):
if l.endswith(n):
remove_new_line = slice(None, -len(n))
return l[remove_new_line]
return l
def _find_furthest_new_line(read_buffer):
"""Return -1 if read_buffer does not contain new line otherwise the position of the rightmost newline.
Args:
read_buffer (bytestring)
Returns:
int: The right most position of new line character in read_buffer if found, else -1
"""
new_line_positions = [read_buffer.rfind(n) for n in new_lines_bytes]
return max(new_line_positions)
def _is_partially_read_new_line(b):
"""Return True when b is part of a new line separator found at index >= 1, False otherwise.
Args:
b (bytestring)
Returns:
bool
"""
for n in new_lines_bytes:
if n.find(b) >= 1:
return True
return False
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""FileReadBackwards module."""
import io
import os
from .buffer_work_space import BufferWorkSpace
supported_encodings = ["utf-8", "ascii", "latin-1"] # any encodings that are backward compatible with ascii should work
class FileReadBackwards:
"""Class definition for `FileReadBackwards`.
A `FileReadBackwards` will spawn a `FileReadBackwardsIterator` and keep an opened file handler.
It can be used as a Context Manager. If done so, when exited, it will close its file handler.
In any mode, `close()` can be called to close the file handler..
"""
def __init__(self, path, encoding="utf-8", chunk_size=io.DEFAULT_BUFFER_SIZE):
"""Constructor for FileReadBackwards.
Args:
path: Path to the file to be read
encoding (str): Encoding
chunk_size (int): How many bytes to read at a time
"""
if encoding.lower() not in supported_encodings:
error_message = "{0} encoding was not supported/tested.".format(encoding)
error_message += "Supported encodings are '{0}'".format(",".join(supported_encodings))
raise NotImplementedError(error_message)
self.path = path
self.encoding = encoding.lower()
self.chunk_size = chunk_size
self.iterator = FileReadBackwardsIterator(io.open(self.path, mode="rb"), self.encoding, self.chunk_size)
def __iter__(self):
"""Return its iterator."""
return self.iterator
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Closes all opened its file handler and propagates all exceptions on exit."""
self.close()
return False
def close(self):
"""Closes all opened it s file handler."""
self.iterator.close()
def readline(self):
"""Return a line content (with a trailing newline) if there are content. Return '' otherwise."""
try:
r = next(self.iterator) + os.linesep
return r
except StopIteration:
return ""
class FileReadBackwardsIterator:
"""Iterator for `FileReadBackwards`.
This will read backwards line by line a file. It holds an opened file handler.
"""
def __init__(self, fp, encoding, chunk_size):
"""Constructor for FileReadBackwardsIterator
Args:
fp (File): A file that we wish to start reading backwards from
encoding (str): Encoding of the file
chunk_size (int): How many bytes to read at a time
"""
self.path = fp.name
self.encoding = encoding
self.chunk_size = chunk_size
self.__fp = fp
self.__buf = BufferWorkSpace(self.__fp, self.chunk_size)
def __iter__(self):
return self
def next(self):
"""Returns unicode string from the last line until the beginning of file.
Gets exhausted if::
* already reached the beginning of the file on previous iteration
* the file got closed
When it gets exhausted, it closes the file handler.
"""
# Using binary mode, because some encodings such as "utf-8" use variable number of
# bytes to encode different Unicode points.
# Without using binary mode, we would probably need to understand each encoding more
# and do the seek operations to find the proper boundary before issuing read
if self.closed:
raise StopIteration
if self.__buf.has_returned_every_line():
self.close()
raise StopIteration
self.__buf.read_until_yieldable()
r = self.__buf.return_line()
return r.decode(self.encoding)
__next__ = next
@property
def closed(self):
"""The status of the file handler.
:return: True if the file handler is still opened. False otherwise.
"""
return self.__fp.closed
def close(self):
"""Closes the file handler."""
self.__fp.close()
Metadata-Version: 2.1
Name: pamela
Version: 1.0.0
Summary: PAM interface using ctypes
Home-page: https://github.com/minrk/pamela
Author: Min RK
Author-email: benjaminrk@gmail.com
License: MIT
Keywords: pam,authentication
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
Description-Content-Type: text/markdown
# Pamela: yet another Python wrapper for PAM
There seems to be a glut of Python wrappers for PAM that have since been abandoned.
This repo merges two separate efforts:
- [gnosek/python-pam](https://github.com/gnosek/python-pam)
- adds wrappers for a few more calls, e.g. opening sessions
- raises PamError on failure instead of returning False, with informative error messages
- [simplepam](https://github.com/leonnnn/python3-simplepam)
- adds Python 3 support
- resets credentials after authentication, apparently for kerberos users
## Why?
Both projects appear to be abandoned, with no response to issues or pull requests in at least a year, and I need it for [JupyterHub](https://github.com/jupyter/jupyterhub).
## Use it
Install:
pip install pamela
Test:
python -m pamela -a `whoami`
__pycache__/pamela.cpython-36.pyc,,
pamela-1.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pamela-1.0.0.dist-info/METADATA,sha256=Jq2Zs9sa58NCeMbwM8d6Oz_cfPEV6eIMLQqQogRgP90,1527
pamela-1.0.0.dist-info/RECORD,,
pamela-1.0.0.dist-info/WHEEL,sha256=J3CsTk7Mf2JNUyhImI-mjX-fmI4oDjyiXgWT4qgZiCE,110
pamela-1.0.0.dist-info/top_level.txt,sha256=sYmDCHiuiyLrWh33a_Rn49nqOVTmFg_MAtDQDbAWwwg,7
pamela.py,sha256=Q-4JPrPImmAArr2hDneNO2Wc40amVY0d8QmHK1XAF-c,15137
Wheel-Version: 1.0
Generator: bdist_wheel (0.31.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any
# (c) 2007 Chris AtLee <chris@atlee.ca>
# (c) 2010 Grzegorz Nosek <root@localdomain.pl>
# (c) 2015 Min RK <benjaminrk@gmail.com>
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
"""
PAM module for python
Provides an authenticate function that will allow the caller to authenticate
a user against the Pluggable Authentication Modules (PAM) on the system.
Implemented using ctypes, so no compilation is necessary.
"""
from __future__ import print_function
__version__ = '1.0.0'
__all__ = [
'PAMError',
'authenticate',
'open_session',
'close_session',
'check_account',
'change_password',
]
from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof, byref
from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
from ctypes.util import find_library
import getpass
import sys
# Python 3 bytes/unicode compat
if sys.version_info >= (3,):
unicode = str
raw_input = input
def _bytes_to_str(s, encoding='utf8'):
return s.decode(encoding)
else:
def _bytes_to_str(s, encoding='utf8'):
return s
def _cast_bytes(s, encoding='utf8'):
if isinstance(s, unicode):
return s.encode(encoding)
return s
LIBPAM = CDLL(find_library("pam"))
LIBC = CDLL(find_library("c"))
CALLOC = LIBC.calloc
CALLOC.restype = c_void_p
CALLOC.argtypes = [c_uint, c_uint]
STRDUP = LIBC.strdup
STRDUP.argstypes = [c_char_p]
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
# Various constants
PAM_PROMPT_ECHO_OFF = 1
PAM_PROMPT_ECHO_ON = 2
PAM_ERROR_MSG = 3
PAM_TEXT_INFO = 4
# These constants are libpam-specific
PAM_ESTABLISH_CRED = 0x0002
PAM_DELETE_CRED = 0x0004
PAM_REINITIALIZE_CRED = 0x0008
PAM_REFRESH_CRED = 0x0010
# constants for PAM_ variables for pam_set_item()
PAM_SERVICE = 1
PAM_USER = 2
PAM_TTY = 3
PAM_RHOST = 4
PAM_RUSER = 8
# PAM error codes
PAM_SUCCESS = 0
PAM_BAD_ITEM = 29
class PamHandle(Structure):
"""wrapper class for pam_handle_t"""
_fields_ = [
("handle", c_void_p)
]
def __init__(self):
Structure.__init__(self)
self.handle = 0
def get_item(self, item_type, encoding='utf-8'):
voidPointer = c_void_p()
retval = PAM_GET_ITEM(self, item_type, byref(voidPointer))
if retval == PAM_BAD_ITEM:
return None
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
s = cast(voidPointer, c_char_p)
if s.value is None:
return None
return _bytes_to_str(s.value, encoding)
def set_item(self, item_type, item, encoding='utf-8'):
retval = PAM_SET_ITEM(self, item_type, item.encode(encoding))
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
def get_env(self, var, encoding='utf-8'):
ret = PAM_GETENV(self, var.encode(encoding))
if ret is None:
raise PAMError()
else:
return ret.decode(encoding)
def put_env(self, k, v, encoding='utf-8'):
retval = PAM_PUTENV(
self,
('%s=%s' % (k, v)).encode(encoding))
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
def del_env(self, k, encoding='utf-8'):
retval = PAM_PUTENV(
self,
k.encode(encoding))
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
def get_envlist(self, encoding='utf-8'):
ret = PAM_GETENVLIST(self)
if ret is None:
raise PAMError()
parsed = {}
for i in PAM_GETENVLIST(self):
if i:
k, v = i.decode(encoding).split('=', 1)
parsed[k] = v
else:
break
return parsed
def open_session(self):
retval = PAM_OPEN_SESSION(self, 0)
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
def close_session(self):
retval = PAM_CLOSE_SESSION(self, 0)
if retval != PAM_SUCCESS:
raise PAMError(errno=retval)
PAM_STRERROR = LIBPAM.pam_strerror
PAM_STRERROR.restype = c_char_p
PAM_STRERROR.argtypes = [PamHandle, c_int]
def pam_strerror(handle, errno):
"""Wrap bytes-only PAM_STRERROR in native str"""
return _bytes_to_str(PAM_STRERROR(handle, errno))
class PAMError(Exception):
errno = None
message = ''
def __init__(self, message='', errno=None):
self.errno = errno
if message:
self.message = message
else:
if errno is None:
self.message = "Unknown"
else:
self.message = pam_strerror(PamHandle(), errno)
def __repr__(self):
en = '' if self.errno is None else ' %i' % self.errno
return "<PAM Error%s: '%s'>" % (en, self.message)
def __str__(self):
en = '' if self.errno is None else ' %i' % self.errno
return '[PAM Error%s] %s' % (en, self.message)
class PamMessage(Structure):
"""wrapper class for pam_message structure"""
_fields_ = [
("msg_style", c_int),
("msg", POINTER(c_char)),
]
def __repr__(self):
return "<PamMessage %i '%s'>" % (self.msg_style, _bytes_to_str(self.msg))
class PamResponse(Structure):
"""wrapper class for pam_response structure"""
_fields_ = [
("resp", POINTER(c_char)),
("resp_retcode", c_int),
]
def __repr__(self):
return "<PamResponse %i '%s'>" % (self.resp_retcode, _bytes_to_str(self.resp))
CONV_FUNC = CFUNCTYPE(c_int,
c_int, POINTER(POINTER(PamMessage)),
POINTER(POINTER(PamResponse)), c_void_p)
class PamConv(Structure):
"""wrapper class for pam_conv structure"""
_fields_ = [
("conv", CONV_FUNC),
("appdata_ptr", c_void_p)
]
PAM_START = LIBPAM.pam_start
PAM_START.restype = c_int
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
POINTER(PamHandle)]
PAM_END = LIBPAM.pam_end
PAM_END.restype = c_int
PAM_END.argtypes = [PamHandle, c_int]
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
PAM_AUTHENTICATE.restype = c_int
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
PAM_OPEN_SESSION = LIBPAM.pam_open_session
PAM_OPEN_SESSION.restype = c_int
PAM_OPEN_SESSION.argtypes = [PamHandle, c_int]
PAM_CLOSE_SESSION = LIBPAM.pam_close_session
PAM_CLOSE_SESSION.restype = c_int
PAM_CLOSE_SESSION.argtypes = [PamHandle, c_int]
PAM_ACCT_MGMT = LIBPAM.pam_acct_mgmt
PAM_ACCT_MGMT.restype = c_int
PAM_ACCT_MGMT.argtypes = [PamHandle, c_int]
PAM_CHAUTHTOK = LIBPAM.pam_chauthtok
PAM_CHAUTHTOK.restype = c_int
PAM_CHAUTHTOK.argtypes = [PamHandle, c_int]
PAM_SETCRED = LIBPAM.pam_setcred
PAM_SETCRED.restype = c_int
PAM_SETCRED.argtypes = [PamHandle, c_int]
PAM_GETENV = LIBPAM.pam_getenv
PAM_GETENV.restype = c_char_p
PAM_GETENV.argtypes = [PamHandle, c_char_p]
PAM_GETENVLIST = LIBPAM.pam_getenvlist
PAM_GETENVLIST.restype = POINTER(c_char_p)
PAM_GETENVLIST.argtypes = [PamHandle]
PAM_PUTENV = LIBPAM.pam_putenv
PAM_PUTENV.restype = c_int
PAM_PUTENV.argtypes = [PamHandle, c_char_p]
PAM_SET_ITEM = LIBPAM.pam_set_item
PAM_SET_ITEM.restype = c_int
PAM_SET_ITEM.argtypes = [PamHandle, c_int, c_char_p]
PAM_GET_ITEM = LIBPAM.pam_get_item
PAM_GET_ITEM.restype = c_int
PAM_GET_ITEM.argtypes = [PamHandle, c_int, POINTER(c_void_p)]
@CONV_FUNC
def default_conv(n_messages, messages, p_response, app_data):
addr = CALLOC(n_messages, sizeof(PamResponse))
p_response[0] = cast(addr, POINTER(PamResponse))
if not sys.stdin.isatty():
return 0
for i in range(n_messages):
msg = messages[i].contents
style = msg.msg_style
msg_string = _bytes_to_str(cast(msg.msg, c_char_p).value)
if style == PAM_TEXT_INFO:
# back from POINTER(c_char) to c_char_p
print(msg_string)
elif style == PAM_ERROR_MSG:
print(msg_string, file=sys.stderr)
elif style in (PAM_PROMPT_ECHO_ON, PAM_PROMPT_ECHO_OFF):
if style == PAM_PROMPT_ECHO_ON:
read_pw = raw_input
else:
read_pw = getpass.getpass
pw_copy = STRDUP(_cast_bytes(read_pw(msg_string)))
p_response.contents[i].resp = pw_copy
p_response.contents[i].resp_retcode = 0
else:
print(repr(messages[i].contents))
return 0
def new_simple_password_conv(passwords, encoding):
passwords = [_cast_bytes(password, encoding) for password in passwords]
passwords.reverse()
@CONV_FUNC
def conv_func(n_messages, messages, p_response, app_data):
"""Simple conversation function that responds to any
prompt where the echo is off with the supplied password"""
# Create an array of n_messages response objects
addr = CALLOC(n_messages, sizeof(PamResponse))
p_response[0] = cast(addr, POINTER(PamResponse))
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
if not passwords:
return 1
pw_copy = STRDUP(passwords.pop())
p_response.contents[i].resp = pw_copy
p_response.contents[i].resp_retcode = 0
return 0
return conv_func
def pam_start(service, username, conv_func=default_conv, encoding='utf8'):
service = _cast_bytes(service, encoding)
username = _cast_bytes(username, encoding)
handle = PamHandle()
conv = pointer(PamConv(conv_func, 0))
retval = PAM_START(service, username, conv, pointer(handle))
if retval != 0:
PAM_END(handle, retval)
raise PAMError(errno=retval)
return handle
def pam_end(handle, retval=0):
e = PAM_END(handle, retval)
if retval == 0 and e == 0:
return
if retval == 0:
retval = e
raise PAMError(errno=retval)
def authenticate(username, password=None, service='login', encoding='utf-8',
resetcred=PAM_REINITIALIZE_CRED, close=True):
"""Returns None if the given username and password authenticate for the
given service. Raises PAMError otherwise
``username``: the username to authenticate
``password``: the password in plain text
Defaults to None to use PAM's conversation interface
``service``: the PAM service to authenticate against.
Defaults to 'login'
The above parameters can be strings or bytes. If they are strings,
they will be encoded using the encoding given by:
``encoding``: the encoding to use for the above parameters if they
are given as strings. Defaults to 'utf-8'
``resetcred``: Use the pam_setcred() function to
reinitialize the credentials.
Defaults to 'PAM_REINITIALIZE_CRED'.
``close``: If True (default) the transaction will be closed after
authentication; if False the (open) PamHandle instance
will be returned.
"""
if password is None:
conv_func = default_conv
else:
password = _cast_bytes(password, encoding)
conv_func = new_simple_password_conv((password, ), encoding)
handle = pam_start(service, username, conv_func=conv_func, encoding=encoding)
retval = PAM_AUTHENTICATE(handle, 0)
# Re-initialize credentials (for Kerberos users, etc)
# Don't check return code of pam_setcred(), it shouldn't matter
# if this fails
if retval == 0 and resetcred:
PAM_SETCRED(handle, resetcred)
if close:
return pam_end(handle, retval)
elif retval != 0:
raise PAMError(errno=retval)
else:
return handle
def open_session(username, service='login', encoding='utf-8'):
handle = pam_start(service, username, encoding=encoding)
return pam_end(handle, PAM_OPEN_SESSION(handle, 0))
def close_session(username, service='login', encoding='utf-8'):
handle = pam_start(service, username, encoding=encoding)
return pam_end(handle, PAM_CLOSE_SESSION(handle, 0))
def check_account(username, service='login', encoding='utf-8'):
handle = pam_start(service, username, encoding=encoding)
return pam_end(handle, PAM_ACCT_MGMT(handle, 0))
def change_password(username, password=None, service='login', encoding='utf-8'):
if password is None:
conv_func = default_conv
else:
# Password x2 to answer the "Retype new UNIX password:" prompt
# TODO: If we're not running as root the first prompt will be
# 'current password' which we will not answer, so this will not work
# in that case.
conv_func = new_simple_password_conv((password, password), encoding)
handle = pam_start(service, username, conv_func=conv_func, encoding=encoding)
return pam_end(handle, PAM_CHAUTHTOK(handle, 0))
if __name__ == "__main__":
import optparse
usage = "usage: %prog [options] [username]"
parser = optparse.OptionParser(usage=usage)
parser.add_option('-a', '--authenticate', dest='authenticate',
action='store_true', help='authenticate user')
parser.add_option('-o', '--open-session', dest='open_session',
action='store_true', help='open session')
parser.add_option('-c', '--close-session', dest='close_session',
action='store_true', help='close session')
parser.add_option('-v', '--validate-account', dest='validate_account',
action='store_true', help='check account validity')
parser.add_option('-p', '--change-password', dest='change_password',
action='store_true', help='change password')
parser.add_option('-s', '--service', dest='service',
action='store', default='login',
help='PAM service to use [default: %default]')
parser.add_option('-P', '--ask-password', dest='ask_password',
action='store_true', help="own password prompt instead of PAM's")
(o, a) = parser.parse_args()
if not (o.authenticate or \
o.open_session or \
o.close_session or \
o.validate_account or \
o.change_password):
parser.error("One of -a, -o, -c, -v or -p is mandatory")
try:
user = a[0]
except IndexError:
user = getpass.getuser()
if o.authenticate:
if o.ask_password:
password = getpass.getpass()
else:
password = None
try:
authenticate(user, password, o.service)
except PAMError as e:
sys.exit(e)
if o.open_session:
try:
open_session(user, o.service)
except PAMError as e:
sys.exit(e)
if o.close_session:
try:
close_session(user, o.service)
except PAMError as e:
sys.exit(e)
if o.validate_account:
try:
check_account(user, o.service)
except PAMError as e:
sys.exit(e)
if o.change_password:
if o.ask_password:
password = getpass.getpass()
else:
password = None
try:
change_password(user, password, o.service)
except PAMError as e:
sys.exit(e)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment