New version with security in transport (https selfsigned), change deps, and preparing web version

This commit is contained in:
2019-11-21 10:58:26 +01:00
parent fb4dca9b4e
commit ec991abeed
12 changed files with 730 additions and 637 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ __pycache__/
cooks/*.yaml
control/
site/
admin/

Binary file not shown.

547
api.py

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# Build 11
# Build 13. Using API 2
param([Int32]$startup=0)
#Write-Host $startup
$srcdir = $PSCommandPath | Split-Path -Parent
@@ -9,7 +9,27 @@ $64bit = [Environment]::Is64BitOperatingSystem
$computerName = [System.Net.Dns]::GetHostName()
$UUID=(Get-CimInstance Win32_ComputerSystemProduct).UUID
$exists = Invoke-RestMethod -Method Get -Uri "$server/get/computerexists?ComputerName=$computerName&UUID=$UUID"
if ((Get-Host | Select-Object Version).Version.Major -lt 6){#Powershell Windows (Not Core)
add-type -TypeDefinition @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
}else{ # Powershell Core
$PSDefaultParameterValues +=
@{'Invoke-RestMethod:SkipCertificateCheck' = $true}
}
$exists = Invoke-RestMethod -Method Post -Uri "$server/get/computerexists?ComputerName=$computerName&UUID=$UUID"
if ($exists.Result -eq "ERROR"){
if ($exists.EXITCODE -eq '3'){
Write-Host "Computer outside database:" $computerName
@@ -45,25 +65,25 @@ $diskResults = @()
$ThisVolume.FreeSpace = $([Math]::Round($disk.FreeSpace / 1GB,2))
$DiskResults += $ThisVolume
}
$DisksData = $DiskResults |ConvertTo-Json
$DisksData = ConvertTo-Json -Depth 4 $DiskResults
$DisksData = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($DisksData)) #I can't enter to database all without encode
$lastUser = (Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName -ComputerName .).UserName
# Send it
$paramInvoke = -join("$server/upd/computer?ComputerName=$computerName&UUID=",$UUID,"&SOVersion=",$SOVersion,"&SOCaption=",$SOData.Caption,"&SOBit=",$SOData.OsArchitecture.Substring(0,2),"&LastConnection=",$Timestamp,"&RAM=",$RAMInstalled,"&RAMFree=",$RAMFree,"&CPUName=",$CPUName,"&LastUser=",$lastUser,"&HDD=",$DisksData)
Invoke-RestMethod -Method Get -Uri $paramInvoke | out-null
Invoke-RestMethod -Method Post -Uri $paramInvoke | out-null
# More info (Only if has to be)
# $paramInvoke = -join("$server/upd/computerhis?ComputerName=$computerName&UUID=",$UUID,"&RAMFree=",$RAMFree,"&RAMUsed=",$RAMUsed,"&Timestamp=",$Timestamp,"&DisksUse=",$disksUse)
# Invoke-RestMethod -Method Get -Uri $paramInvoke | out-null
# Invoke-RestMethod -Method Post -Uri $paramInvoke | out-null
# Load Cooks of computer
$cooks = Invoke-RestMethod -Method Get -Uri "$server/get/cookpend?ComputerName=$computerName&UUID=$UUID"
$cooks = Invoke-RestMethod -Method Post -Uri "$server/get/cookpend?ComputerName=$computerName&UUID=$UUID"
foreach ($CookName in $cooks){
Set-Location $env:temp # For downloading/copying items, use system temp location
$CookName = $CookName.CookName
$cook = Invoke-RestMethod -Method Get -Uri "$server/load/cook?CookName=$CookName&ComputerName=$computerName&UUID=$UUID"
$cook = Invoke-RestMethod -Method Post -Uri "$server/load/cook?CookName=$CookName&ComputerName=$computerName&UUID=$UUID"
Write-Host "Receta:" $cook.name
$CookRevision = $cook.revision
$atstartup = $cook.atstartup
@@ -261,7 +281,7 @@ foreach ($CookName in $cooks){
if ($cook.runever -eq "1"){
$err = -1 # This is for run but not for error, is by cook saw.
}
Invoke-RestMethod -Method Get -Uri "$server/upd/cookstatus?ComputerName=$computerName&UUID=$UUID&CookName=$CookName&Revision=$CookRevision&Error=$err&ErrorDesc=$errvar" | out-null
Invoke-RestMethod -Method Post -Uri "$server/set/cookstatus?ComputerName=$computerName&UUID=$UUID&CookName=$CookName&Revision=$CookRevision&Error=$err&ErrorDesc=$errvar" | out-null
#Delete files copied to temp for saving space
foreach ($element in $filesCopied) {

View File

@@ -1,3 +1,3 @@
server=http://miserver.dominio:3333
server=https://miserver.dominio:3333
resources=\\\\MISERVER\\REPOFOLDER
lang=en

View File

@@ -12,6 +12,13 @@ from translation import T
passha256 = None # Password (Global)
config = configparser.ConfigParser()
config.read('configpcm.ini')
c_server = config['General']['server']
c_resources = config['General']['resources'].replace('\\\\','\\')
def ping(host):
"""
Returns True if host (str) responds to a ping request.
@@ -30,6 +37,10 @@ def sendmenudot(text,title,choices):
return menu.split('.')[0]
else:
return menu
def retOpt(option):
return returnvalueapi('/get/optionvalue?Option='+str(option),field="Value")
## showchoicesapi, suburl has to have params (NOT inlcuding server part)
def showchoicesapi(text,title,suburl,field=['Name']):
global c_server
@@ -43,7 +54,8 @@ def showchoicesapi(text,title,suburl,field=['Name']):
else:
twopart= '?Password='+str(passha256)
try:
jsonobj = json.loads(requests.get(c_server+suburl+twopart).text)
r=(requests.post(c_server+suburl+twopart, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
jsonobj = json.loads(r)
listitems = []
for ite in jsonobj: # Run in array from json
to = ""
@@ -66,8 +78,8 @@ def sendsettoapi(suburl,goodtext): # Send a add/del/modify to API. Doesn't retur
else:
twopart= '?Password='+str(passha256)
try:
jsonobj = json.loads(requests.get(c_server+suburl+twopart).text)
#jsonobj['RESULT'] has to exist for next if
r=(requests.post(c_server+suburl+twopart, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
jsonobj = json.loads(r)
try:
jsonobj['RESULT']
except:
@@ -95,22 +107,16 @@ def returnvalueapi(suburl,field="Name"):
else:
twopart= '?Password='+str(passha256)
try:
jsonobj = json.loads(requests.get(c_server+suburl+twopart).text)
r=(requests.post(c_server+suburl+twopart, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
jsonobj = json.loads(r)
return jsonobj[field]
except:
easygui.msgbox(msg=T('Error talking with API'), title="Error", ok_button='OK')
return None
config = configparser.ConfigParser()
config.read('configpcm.ini')
c_server = config['General']['server']
c_resources = config['General']['resources'].replace('\\\\','\\')
def menuprinc():
menu = sendmenudot(T('Main menu'),T('Select option'),['1.'+T('Computers'),'2.'+T('Groups'),'3.'+T('Cooks'),'4.Reportes/Listados'])
menu = sendmenudot(T('Main menu'),T('Select option'),['1.'+T('Computers'),'2.'+T('Groups'),'3.'+T('Cooks'),'4.Reportes/Listados','5.'+T('Options')])
if menu == '1':
mcomputers()
elif menu == '2':
@@ -119,6 +125,8 @@ def menuprinc():
mcooks()
elif menu == '4':
mreports()
elif menu == '5':
moptions()
def mcomputers():
menu = sendmenudot(T('Computers menu'),T('Select option'),['1.'+T('Add computer'),'2.'+T('Remove computer'),'3.'+T('Add computer to group'),'4.'+T('Computers list'),'5.'+T('Computer status')])
@@ -177,7 +185,8 @@ def mcooks():
cook = showchoicesapi("Lista de recetas implementadas en algún grupo. Selecciona una para ver sus datos","Detalles de una receta","/get/cookall",'CookName')
if cook is not None:
showchoicesapi("Lista de grupos de la receta "+cook,"Listado de grupos de una receta","/get/grpcook?CookName="+cook)
jsonobj = json.loads(requests.get(c_server+'/get/statuscook?CookName='+cook).text)
r=(requests.post(c_server+'/get/statuscook?CookName='+cook, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
jsonobj = json.loads(r)
list = []
s_err = 0
s_com = 0
@@ -248,6 +257,20 @@ def mreports():
return
mreports()
def moptions():
debugsql = retOpt('DebugSQL')
addcomputers = retOpt('AddComputers')
menu = sendmenudot(T('Options menu'),T('Select option'),['1.'+T('Debug SQL commands')+'-'+str(debugsql),'2.'+T('Allow auto add of new computers')+'-'+str(addcomputers)])
if menu == '1':
showchoicesapi(T('Computers list'),T('Computers list'),"/get/computers")
elif menu == '2':
showchoicesapi(T('Groups list'),T('Groups list'),"/get/groups")
elif menu is None:
menuprinc()
return
mreports()
def m_addcomputergroup():
whatc = showchoicesapi(T('Select computer to add to a group'),T('Add computer to group'),"/get/computers")
@@ -272,6 +295,7 @@ def m_delcomputergroup():
# Check password before starting all
pa = returnvalueapi("/check/password?Password=None",field="EXITCODE")
print(pa)
if pa == "3": # No password stored yet
passw = easygui.passwordbox(T('There is no password yet in PCM. Please set one below'),T('Set password'))
passw2 = easygui.passwordbox(T('Confirm it'),T('Set password'))
@@ -292,6 +316,7 @@ else: # There is a password
quit()
passha256 = hashlib.sha256(passw.encode()).hexdigest()
re = returnvalueapi("/check/password?Password="+passha256,field="EXITCODE") # Create password in database
print(re)
if re == '0': # Password is right
menuprinc()
else:

10
docs/changelog.md Normal file
View File

@@ -0,0 +1,10 @@
# Changelog
## 2019/11/20
- New API (2). All requests now works in POST and SSL (Self-Signed)
- Now using Flask and not falcon with waitress-serve (Lesser dependencies and ssl support)
- Some new options
- More stable (Besides this API is at beta stage because POST and different json returns)
- Better doc
- Preparing for better gui (Web one)
- Now SQL will have versions, for updating if neccesary (If you are using old version, please create in database new table "OPTIONS" with data that has "emptydatabase.db")

View File

@@ -14,7 +14,6 @@ It's not error proof, then, I don't have any responsability if something crashes
# Roadmap
- Complete translation English and Spanish
- Somewhat more security in API 1
- Stabilize all
- I created a simple GUI in EasyGUI to be easy used, but if I have time, maybe will do a webUI more dynamic and visually better (Without needing to install php/apache/nginx, using python)
- Support Linux clients
@@ -22,12 +21,12 @@ It's not error proof, then, I don't have any responsability if something crashes
# Requirements
## Server
For server you can use Linux or Windows, and in theory any environment that can run Python 3 can work as server
For server you can use Linux, Mac or Windows, and in theory any environment that can run Python 3 can work as server
- Python3 with:
- Falcon
- Waitress-serve
- Flask
- pyopenssl
- Yaml
Python3 deps can be installed it using pip install command (pip install waitress falcon pyyaml)
Python3 deps can be installed it using pip install command (pip install pyopenssl flask pyyaml)
- For controlling the program, you have to start control.pyw using Python3. It doesn't need to be started in server, but it has to be in that folder (Or with configpcm.ini file).
It uses easygui of python3 (pip install easygui)
@@ -47,7 +46,6 @@ For server you can use Linux or Windows, and in theory any environment that can
## Server
- Copy this folder to a folder in your server
- Install dependencies
- Open sysopt.py file and change values if needed
- Start the server: Start api.py using loadserver.bat (Windows) o loadserver.sh (Linux) file. You can do in systemd way, or a cron, or in a terminal of windows server,...
## Client. Example using GPO and Task Schedule
@@ -66,4 +64,4 @@ For server you can use Linux or Windows, and in theory any environment that can
- Args: -executionpolicy bypass -windowstyle hidden -noninteractive -nologo -file "\\SERVER\SysVol\DOMAINNAME\scripts\client.ps1" -startup 1
## Control app
- Start control.pyw to setup password and start adding computers, groups and cooks. You can use docs/example_cooks for examples. If anyone wants, I will setup a git repo for cooks.
- Start control.pyw to setup password and start adding computers, groups and cooks and configure options. You can use docs/example_cooks for examples. If anyone wants, I will setup a git repo for cooks.

View File

@@ -1,3 +1,3 @@
REM Starting Server. Will only work if python and dependencies are installed and in PATH
cd /D "%~dp0"
waitress-serve --port=3333 api:api
python3 api.py

View File

@@ -1,3 +1,3 @@
#!/bin/bash
cd "$(dirname "$(realpath "$0")")";
waitress-serve --port=3333 api:api
python3 api.py

View File

@@ -4,4 +4,5 @@ nav:
- Quick Start: quick_start.md
- How to write a cook: howto_writecook.md
- Hierarchy: file_hierarchy.md
- Changelog: changelog.md
theme: readthedocs

5
sql.py
View File

@@ -55,3 +55,8 @@ def retOption(option):
return None
debugsql = retOption('DebugSQL')
#Make updates if neccesary
sqlv=retOption('SQLVersion')
if sqlv == 1:
pass #Its latest version of SQL