Como posso proteger o meu Python codebase para que os hóspedes não podem ver alguns módulos, mas por isso ainda funciona?
-
22-07-2019 - |
Pergunta
Estamos começando um novo projeto em Python com alguns algoritmos proprietários e pedaços sensíveis da lógica que nós gostaríamos de manter privadas. Teremos também algumas pessoas de fora (selecionar os membros do público) que trabalham no código. Não podemos conceder o acesso de pessoas de fora para os pequenos pedaços, privados de código, mas gostaria de uma versão pública para o trabalho bem o suficiente para eles.
dizer que o nosso projecto, Foo, tem um módulo, bar
, com uma função, get_sauce()
. O que realmente acontece em get_sauce()
é secreto, mas queremos uma versão pública do get_sauce()
para retornar um aceitável, embora incorreto, resultado.
Nós também executar o nosso próprio servidor Subversion por isso temos o controle total sobre quem pode acessar o quê.
Symlinks
O meu primeiro pensamento foi criar um link simbólico - Em vez de bar.py
, fornecer bar_public.py
a todos e bar_private.py
apenas para desenvolvedores internos. Infelizmente, a criação de links simbólicos é, trabalho manual tedioso -. Especialmente quando há realmente vai ser cerca de duas dezenas desses módulos privadas
Mais importante, ele faz o gerenciamento do arquivo authz Subversion difícil, uma vez que para cada módulo queremos proteger uma exceção deve ser adicionado no servidor. Alguém pode esquecer de fazer isso e, acidentalmente, o check-in segredos ... Em seguida, o módulo está no repo e temos que reconstruir o repositório sem ele e esperança de que um estranho não baixá-lo nesse meio tempo.
vários repositórios
O próximo pensamento era ter dois repositórios:
private
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py
A idéia é que apenas os desenvolvedores internos será capaz de fazer o checkout tanto private/
e public/
. desenvolvedores internos irá definir sua PYTHONPATH=private/trunk:public/trunk
, mas todo mundo vai apenas definir PYTHONPATH=public/trunk
. Em seguida, ambos os insiders e outsiders pode from foo import bar
e obter o módulo certo, certo?
Vamos tentar isso:
% PYTHONPATH=private/trunk:public/trunk python
Python 2.5.1
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo.bar
>>> foo.bar.sauce()
'a private bar'
>>> import foo.quux
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named quux
Eu não sou um especialista em Python, mas parece que Python já fez a sua mente sobre o módulo foo
e pesquisas relativas a esse:
>>> foo
<module 'foo' from '/path/to/private/trunk/foo/__init__.py'>
Nem mesmo apagar foo
ajuda:
>>> import sys
>>> del foo
>>> del sys.modules['foo']
>>> import foo.quux
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named quux
Você pode me fornecer uma solução melhor ou sugestão?
Solução
Portanto, criar um diretório chamado secret
e colocá-lo em seu repositório Subversion privado. Em secret
colocar o seu bar.py
proprietário. No __init__.py
do pacote foo
público colocar em algo como:
__path__.insert(0,'secret')
Isto significa para os usuários que têm o repositório privado e assim o diretório secret
que irão receber o bar.py
proprietário como foo.bar
como secret
é o primeiro diretório no caminho de pesquisa. Para outros usuários, Python não vai encontrar secret
e vai olhar como o próximo diretório no __path__
e assim vai carregar o bar.py
normal a partir foo
.
Por isso, será algo parecido com isto:
private
└── trunk/
└── secret/
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py
Outras dicas
Use algum tipo de sistema de plugins, e manter seus plugins para o seu auto, mas também tem plugins publicamente disponíveis que fica enviados com o código aberto.
sistemas Plugin abundam. Você pode facilmente fazer mais simples morto mesmo. Se você quiser algo mais avançado eu prefiro a arquitetura Zope componente, mas também há opções como Setuptools entry_points, etc.
Qual deles usar no seu caso seria uma boa segunda pergunta.
Aqui está uma solução alternativa que eu notei ao ler a documentação para Flask :
flaskext/__init__.py
A única finalidade deste arquivo é para marcar o pacote como pacote namespace. Isso é necessário para que vários módulos de diferentes pacotes PyPI pode residir no mesmo pacote Python:
__import__('pkg_resources').declare_namespace(__name__)
Se você quer saber exatamente o que está acontecendo lá, verificação geral a distribuir ou setuptools docs que explicar como isso funciona.