Package: bitwardentools
tools for working with vaultwarden/bitwarden (_rs) and vaultierThis package containers a python3+ client for bitwarden which uses both a native python implementation but also wraps the official the official npm@bitwarden/cli.The ultimate goal is certainly only to rely on python implementation against the vaultwarden/bitwarden_rs server implementation.Featuresapi controllable clientCreate, Read, Update, Delete,  on organizations, collection, ciphers, users (also disable/enable), and attachmentsAttach Ciphers to organization collectionsSet access at orgas, collections and users levels.Download/Upload attachments to vault and organizationsThe client also integrate a thin wrapper to official npm CLI (seecallmathod)Readapifor longer detailsinstall as a python libpipinstallbitwardentoolsRun in devConfigurecp.env.dist.env
cp.env.local.dist.env.localprintf"USER_UID=$(id-u)\nUSER_GID=$(id-g)\n">>.envBuildeval$(egrep-hv'^#|^\s*$'.env.env.local|sed-e"s/^/export /g"|sed-e"s/=/='/"-e"s/$/'/g"|xargs)COMPOSE_FILE="docker-compose.yml:docker-compose-build.yml"docker-composebuildRundocker-composerun--rmappbashsed-i-e"/COMPOSE_FILE/d".envecho"COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml">>.env
docker-composeup-d--force-recreate
docker-composeexec-uappappbashrun testssed-i-e"/COMPOSE_FILE/d".envecho"COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml:docker-compose-test.yml">>.env
docker-composeexec-uappapptox-elinting,coverageCredits and bibliographygnunuxexcellent articles:123https://github.com/dani-garcia/vaultwarden/(old:https://github.com/dani-garcia/bitwarden_rs/)https://github.com/doy/rbw/tree/master/srchttps://github.com/bitwarden/jslibhttps://github.com/birlorg/bitwarden-cli/tree/trunk/python/bitwardenhttps://github.com/jcs/rubywardenDocsee alsoUSAGE(or read below on pypi)CHANGES1.0.57QA & CI/CD fixes [kiorky]Fix newer vaultwarden patch [kiorky]Fix newer vaultwarden adduser [kiorky]Fix new vaultwarden create_orga [kiorky]Fix newer vaultwarden set_org_acces [kiorky]1.0.56Customizable auth payload support (2Factor, api auth) [Markus Kötterkoetter@cispa.de])1.0.55ensure requests is in requirements [kiorky]1.0.54bugfix: exclude folderId from getting encrypted [Thomas Kriechbaumerthomas@kriechbaumer.name]1.0.53kdfIterations payload change fix [kiorky]add delete_user [kiorky]1.0.51Do not need private key for confirming users
[Didier 'OdyX' Rabouddidier.raboud@liip.ch]1.0.49complete vaultierAS_SINGLE_ORG=falseaclsfeed collections accesses also with globalaccessAll=trueusers.1.0.47vaultier migration: add notify scriptvaultier migration: finish cycleAdd orga/collection memberships managment methodsRename tokens attributeBetter error messagfesOptimize login & token managementCache overhaul and factorizationVaultier AsOneOrganization import variantsClarify docs1.0.46Compatibility leftovers with bitwarden_rs1.20.1.0.45Compatibility with bitwarden_rs1.20(was1.18).1.0.44initial releaseCut a release./release.sh$versionUsageclient=Client(server,email,password)client.sync()## direct object creation methods# organizationclient.create_organization('foo','foo@foo.com')# collectionclient.create_collection('bar',orga='foo')# default item/loginpayload={"notes":"supernote","login":{"totp":"aze",'username':"alice","password":"rabbit","uris":[{"match":None,"uri":"http://a"}]}}client.create_item("sec5",orga,collections=[col],**payload)# if orga is None cipher will go inside user vaultclient.create_item("secpersonal",**payload)## is a synoym: client.create_login# identity# title": "Mr/Mrs/Ms/Dr"payload={"identity":{"address1":"foo","address2":"foo","address3":"foo","city":"foo","postalCode":"foo","country":"foo","state":"foo","username":"foo","company":"foo","phone":"foo","email":"foo","title":"Mrs","firstName":"foo","lastName":"foo","middleName":"foo","ssn":"foo","licenseNumber":"foo","passportNumber":"foo",},"notes":"foo",}client.create_identity("sec1",orga,collections=[col],**payload)# notepayload={"fields":[{"name":"thisisabool","type":2,"value":False}],"notes":"notenote","secureNote":{"type":0},}client.create_securenote("sec2",orga,collections=[col],**payload)# cardpayload={"card":{"brand":"sec","cardholderName":"foo","number":"aaa","code":"123456","expMonth":"10","expYear":"2013"},"fields":[{"name":"aaa","type":0,"value":"aaa"}],"notes":"aaa"}client.create_card("sec4",orga,collections=[col],**payload)## create only with json payloadsorga=client.create(**{'object':'organization','name':"org",'email':email})# Create a collectioncol=client.create(**{'object':'org-collection','name':"testcol",'organizationId':client.item_or_id(orga)})col2=client.create(**{'object':'org-collection','name':"testcol2",'organizationId':client.item_or_id(orga)})# Create a login within an organization, collectionIds is mandatory on bitwarden_rs 1.19+cipher=client.create(**{"name":"test","object":"item","organizationId":orga.id,"notes":"supernote","login":{'username':"alice","password":"rabbit"},"collectionIds":[col2.id],})# Create a login within your personal vaultcipher=client.create(**{"name":"test","object":"item","notes":"supernote","login":{'username':"alice","password":"rabbit"})## Patch existing objectstestorg=client.get_organization("org")client.edit_organization(testorg,name='fooorg')#testcol=client.get_collection("testcol")client.edit_orgcollection(testcol,name='foocol')## Play with ciphersall_ciphers=client.get_ciphers()cipher=client.get_cipher("test",collection=col,orga=orga)# Put cipther in collection col2client.link(cipher,col2)## Attachmentsclient.attach(sec,"/path/to/foo.zip")# reload cipher with it's new attachment# default dir in current working directory, default filename is uploaded filenameclient.download(sec.attachments[0],directory='/w/data/titi/toto',filename='tata.zip')client.delete_attachments(sec)## users management#users=client.get_users()# > {"emails": {}, "ids": {}, "names": {}} users indexed dicts# search one useruser=client.get_user(email="foo@bar.com")user=client.get_user(name="foo")user=client.get_user(id="424242424-4242-4242-4242-424242424242")# enable/delete/disable methods can take id/email/name or user instances as kwargs:client.disable_user(email="foo@bar.com")client.disable_user(id="424242424-4242-4242-4242-424242424242")client.disable_user(name="foo")client.disable_user(user=user)# other methodsclient.enable_user(user=/name=/id=/email=)client.delete_user(user=/name=/id=/email=)# if not password, it will be autogenerated and in the return tupleuser,pw=client.create_user('foo@bar.com',password=,passwordhint=,name=)# If you use bitwarden_rs and you setted up the bitwarden rs key,# the user will be automatically validated# you can manually validate an account with:user=client.validate('foo@bar.com')# you can also manage orgs invitationsacl=client.accept_invitation('foo@bar.com',orga)# need bitwarden server private keyacl=client.confirm_invitation('foo@bar.com',orga)# need bitwarden server private key# you can also manage collection permissions## add user to orgac.add_user_to_organization(user,orga,collections=col)## set them at orga level (will add to orga if not already member)c.set_organization_access(user,orga,collections=col,hidepasswords=False,readOnly=True/False)c.set_organization_access(user,orga,{"collection":col,"hidePasswords":False},hidepasswords=True)## add them at collection levelc.set_collection_access(user,col,hidepasswords=True/False,readOnly=True/False)## remove from collection: colc.set_organization_access(user,orga,{"collection":col,"remove":True})### orc.set_collection_access(user,{"collection":col,"remove":True})## get acls infosc.get_accesses(orga)c.get_accesses(col)c.get_accesses({"user":user,"collection":col})c.get_accesses({"user":user,"orga":orga})## remove from collectionc.remove_user_from_collection(userOrEmail,colc)## remove from orgac.remove_user_from_organization(userOrEmail,orga)Manipulating the login data structure via callback (2Factor)This allows other login mechanisms such as totp or api key:example 1:defmfa2fa(loginpayload):totp=pyotp.TOTP(otpseed)loginpayload.update({"twoFactorToken":str(totp.now()),"twoFactorProvider":"0","twoFactorRemember":"0"})returnloginpayloadclient=Client(server,email,password,authentication_cb=mfa2fa)example 1:defapi_key(loginpayload):loginpayload.update({"client_id":CLIENT_ID,"client_secret":CLIENT_SECRET,"scope":"api","grant_type":"client_credentials"})returnloginpayloadclient=Client(server,email,password,authentication_cb=api_key)encode the vaultwarden/bitwarden_rs key for autovalidating userbase64$BITWARDEN_RS_SERVER_DATA/rsa_key.der|tr-d'\n'=>copypastetheresultinyour.env.localthiswayBITWARDEN_PRIVATE_KEY=MIIxxxmigrate from vaultier to bitwarden notesVAULTIER_KEY=$(echo$(base64~/vaultier_key.bin|tr-d'\n')cat>>.env<< EOFVAULTIER_KEY=${VAULTIER_KEY}# if your vauiltier has aditionnal httpauth# VAULTIER_HTTP_PASSWORD=htpasswd# VAULTIER_HTTP_USER=userVAULTIER_EMAIL=myvaultier.email@d.comVAULTIER_URL=https://vaultier.foo.netVAULTIER_JSON=data/export/vaultierfile.jsonBW_ORGA_NAME=MyBitwardenOrgaBITWARDEN_PW=MasterPasswordBITWARDEN_SERVER=https://bitwd.foo.netBITWARDEN_EMAIL=foo@foo.comexport vaultier data to json file for cards and files for attachmentsIt will produce data/export/vaultname.jsonAnd download attachments inside data/export/secret$id/timepythonsrc/bitwardentools/vaultier/export.pyload vaultier json serialized vaults/cards into bitwarden orga/collectionsAs bitwarden has only 2 folds, where vaultier has 3, cards are migrated into bitwarden and named$vault $card; this is the link between the two systems, please do not rename your card as long as you want to continue to migrate or it will duplicate things.timepythonsrc/bitwardentools/vaultier/import_structure.pysync secretstimepythonsrc/bitwardentools/vaultier/sync_secrets.pyload vaultier json members as bitwarden users Profiles and tie them to their secretspythonsrc/bitwardentools/vaultier/invite.pyNotify users of their accountspythonsrc/bitwardentools/vaultier/notify.py--dry-run=0Security noteWe provide abitwardentools.client.bust_cachemethod to invalidate any cache in memory, please use it whenever you have finished to access your secrets.frombitwardentools.clientimportbust_cachebust_cache()