Come posso proteggere la mia base di codice Python in modo che gli ospiti non possano vedere determinati moduli ma funzioni ancora?

StackOverflow https://stackoverflow.com/questions/1443146

Domanda

Stiamo iniziando un nuovo progetto in Python con alcuni algoritmi proprietari e bit sensibili di logica che vorremmo mantenere privati. Avremo anche alcuni outsider (membri selezionati del pubblico) che lavorano al codice. Non possiamo garantire agli estranei l'accesso a piccoli frammenti di codice privati, ma vorremmo che una versione pubblica funzionasse abbastanza bene per loro.

Supponi che il nostro progetto, Foo, abbia un modulo, bar , con una funzione, get_sauce () . Ciò che accade realmente in get_sauce () è segreto, ma vogliamo che una versione pubblica di get_sauce () restituisca un risultato accettabile, sebbene non corretto.

Eseguiamo anche il nostro server Subversion in modo da avere il controllo totale su chi può accedere a cosa.

link simbolici

Il mio primo pensiero è stato il collegamento simbolico - & nbsp; Invece di bar.py , fornire bar_public.py a tutti e bar_private.py all'interno solo sviluppatori. Sfortunatamente, creare collegamenti simbolici è un lavoro noioso e manuale, specialmente quando ci saranno davvero circa due dozzine di questi moduli privati.

Ancora più importante, rende difficile la gestione del file authz di Subversion, poiché per ogni modulo che vogliamo proteggere un'eccezione deve essere aggiunta sul server. Qualcuno potrebbe dimenticare di farlo e controllare accidentalmente i segreti ... Quindi il modulo è nel repository e dobbiamo ricostruire il repository senza di esso e sperare che un estraneo non lo abbia scaricato nel frattempo.

Repository multipli

L'idea successiva era di avere due repository:

private
└── trunk/
    ├── __init__.py
    └── foo/
        ├── __init__.py
        └── bar.py
public
└── trunk/
    ├── __init__.py
    └── foo/
        ├── __init__.py
        ├── bar.py
        ├── baz.py
        └── quux.py

L'idea è che solo gli sviluppatori interni saranno in grado di effettuare il checkout sia su private / che public / . Gli sviluppatori interni imposteranno il loro PYTHONPATH = private / trunk: public / trunk , ma tutti gli altri imposteranno semplicemente PYTHONPATH = public / trunk . Quindi, sia gli addetti ai lavori che gli estranei possono dalla barra di importazione foo e ottenere il modulo giusto, giusto?

Proviamo questo:

% 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

Non sono un esperto di Python, ma sembra che Python abbia già deciso sul modulo pippo e ricerche relative a questo:

>>> foo
<module 'foo' from '/path/to/private/trunk/foo/__init__.py'>

Neanche cancellare foo aiuta:

>>> 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

Potete fornirmi una soluzione o un suggerimento migliore?

È stato utile?

Soluzione

Nel metodo __init__ del pacchetto foo puoi cambiare __path__ per farlo cercare i suoi moduli in altre directory.

Quindi crea una directory chiamata secret e inseriscila nel tuo repository Subversion privato. In secret inserisci il tuo bar.py proprietario. Nel pacchetto __init__.py del pacchetto foo pubblico inserisci qualcosa come:

__path__.insert(0,'secret')

Questo significherà per gli utenti che hanno il repository privato e quindi la directory secret otterranno il bar.py proprietario come foo.bar as secret è la prima directory nel percorso di ricerca. Per altri utenti, Python non troverà secret e apparirà come la directory successiva in __path__ e quindi caricherà il normale bar.py da foo .

Quindi sarà simile a questo:

   private
    └── trunk/
        └── secret/
            └── bar.py
    public
    └── trunk/
        ├── __init__.py
        └── foo/
            ├── __init__.py
            ├── bar.py
            ├── baz.py
            └── quux.py

Altri suggerimenti

Usa una sorta di sistema di plugin e mantieni i tuoi plugin per te, ma hai anche plugin disponibili pubblicamente che vengono spediti con il codice aperto.

I sistemi plug-in abbondano. Puoi facilmente rendere semplici quelli morti. Se vuoi qualcosa di più avanzato preferisco Zope Component Architecture, ma ci sono anche opzioni come setuptools entry_points, ecc.

Quale da usare nel tuo caso sarebbe una buona seconda domanda.

Ecco una soluzione alternativa che ho notato leggendo i documenti per Flask :

  

flaskext / __ init __. Py

     

L'unico scopo di questo file è di contrassegnare il pacchetto come pacchetto dello spazio dei nomi. Questo è necessario affinché più moduli di diversi pacchetti PyPI possano risiedere nello stesso pacchetto Python:

__import__('pkg_resources').declare_namespace(__name__)
     

Se vuoi sapere esattamente cosa sta succedendo lì, dai un'occhiata ai documenti di distribuzione o setuptools che spiegano come funziona.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top