def POST(self): """ Test authentication """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') return response_render('OK')
def POST(self): """ Test authentication """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') return response_render('OK')
def GET(self): """ Return ca. """ return response_render( open(SERVER_OPTS['ca'] + '.pub', 'rb'), content_type='application/octet-stream')
def GET(self): """ Return ca. """ return response_render( open(SERVER_OPTS['ca'] + '.pub', 'rb'), content_type='application/octet-stream')
def GET(self): """ Return a health check """ health = {} health['name'] = 'cassh' health['version'] = VERSION return response_render( dumps(health, indent=4, sort_keys=True), content_type='application/json')
def GET(self): """ Return a health check """ health = {} health['name'] = 'cassh' health['version'] = VERSION return response_render( dumps(health, indent=4, sort_keys=True), content_type='application/json')
def DELETE(self, username): """ Delete keys (but DOESN'T REVOKE) /admin/<username> """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('DELETE FROM USERS WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK')
def DELETE(self, username): """ Delete keys (but DOESN'T REVOKE) /admin/<username> """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('DELETE FROM USERS WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK')
def GET(self): """ /cluster/status """ message = dict() alive_nodes, dead_nodes = TOOLS.cluster_alived() for node in alive_nodes: message.update({node: {'status': 'OK'}}) for node in dead_nodes: message.update({node: {'status': 'KO'}}) return response_render( dumps(message), content_type='application/json')
def POST(self): """ Get client key status. /client/status """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') return response_render( TOOLS.list_keys(realname=realname), content_type='application/json')
def POST(self): """ Get client key status. /client/status """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') return response_render( TOOLS.list_keys(realname=realname), content_type='application/json')
def GET(self): """ /cluster/status """ message = dict() alive_nodes, dead_nodes = TOOLS.cluster_alived() for node in alive_nodes: message.update({node: {'status': 'OK'}}) for node in dead_nodes: message.update({node: {'status': 'KO'}}) return response_render( dumps(message), content_type='application/json')
def PATCH(self, username): """ Set the first founded value. /admin/<username> key=value => Set the key value. Keys are in status output. """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() payload = data2map() for key, value in payload.items(): if key == 'expiry': pattern = re_compile('^\\+([0-9]+)+[dh]$') if pattern.match(value) is None: return response_render( 'ERROR: Value %s is malformed. Should match pattern ^\\+([0-9]+)+[dh]$' \ % value, http_code='400 Bad Request') cur.execute('UPDATE USERS SET EXPIRY=(%s) WHERE NAME=(%s)', (value, username)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK: %s=%s for %s' % (key, value, username)) elif key == 'principals': value = unquote_plus(value) pattern = re_compile("^([a-zA-Z]+)$") for principal in value.split(','): if pattern.match(principal) is None: return response_render( 'ERROR: Value %s is malformed. Should match pattern ^([a-zA-Z]+)$' \ % principal, http_code='400 Bad Request') cur.execute('UPDATE USERS SET PRINCIPALS=(%s) WHERE NAME=(%s)', (value, username)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK: %s=%s for %s' % (key, value, username)) return response_render('WARNING: No key found...')
def PATCH(self, username): """ Set the first founded value. /admin/<username> key=value => Set the key value. Keys are in status output. """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() payload = data2map() for key, value in payload.items(): if key == 'expiry': pattern = re_compile('^\\+([0-9]+)+[dh]$') if pattern.match(value) is None: return response_render( 'ERROR: Value %s is malformed. Should match pattern ^\\+([0-9]+)+[dh]$' \ % value, http_code='400 Bad Request') cur.execute('UPDATE USERS SET EXPIRY=(%s) WHERE NAME=(%s)', (value, username)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK: %s=%s for %s' % (key, value, username)) elif key == 'principals': value = unquote_plus(value) pattern = re_compile("^([a-zA-Z-]+)$") for principal in value.split(','): if pattern.match(principal) is None: return response_render( 'ERROR: Value %s is malformed. Should match pattern ^([a-zA-Z-]+)$' \ % principal, http_code='400 Bad Request') cur.execute('UPDATE USERS SET PRINCIPALS=(%s) WHERE NAME=(%s)', (value, username)) pg_conn.commit() cur.close() pg_conn.close() return response_render('OK: %s=%s for %s' % (key, value, username)) return response_render('WARNING: No key found...')
def PUT(self): """ This function permit to add or update a ssh public key. /client username=xxxxxx => Unique username. Used by default to connect on server. [email protected] => This LDAP/AD user. """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'username' in payload: username = payload['username'] else: return response_render( 'Error: No username option given.', http_code='400 Bad Request') username_pattern = re_compile("^([a-z]+)$") if username_pattern.match(username) is None or username == 'all': return response_render( "Error: Username doesn't match pattern %s" \ % username_pattern.pattern, http_code='400 Bad Request') if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') realname_pattern = re_compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', IGNORECASE) if realname_pattern.match(realname) is None: return response_render( "Error: Realname doesn't match pattern", http_code='400 Bad Request') # Get public key if 'pubkey' in payload: pubkey = unquote_custom(payload['pubkey']) else: return response_render( 'Error: No pubkey given.', http_code='400 Bad Request') tmp_pubkey = NamedTemporaryFile(delete=False) tmp_pubkey.write(bytes(pubkey, 'utf-8')) tmp_pubkey.close() pubkey_fingerprint = get_fingerprint(tmp_pubkey.name) if pubkey_fingerprint == 'Unknown': remove(tmp_pubkey.name) return response_render( 'Error : Public key unprocessable', http_code='422 Unprocessable Entity') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: remove(tmp_pubkey.name) return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username,)) user = cur.fetchone() # CREATE NEW USER if user is None: cur.execute('INSERT INTO USERS VALUES \ ((%s), (%s), (%s), (%s), (%s), (%s), (%s), (%s))', \ (username, realname, 2, 0, pubkey_fingerprint, pubkey, '+12h', '')) pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Create user=%s. Pending request.' % username, http_code='201 Created') else: # Check if realname is the same cur.execute('SELECT * FROM USERS WHERE NAME=(%s) AND REALNAME=lower((%s))', \ (username, realname)) if cur.fetchone() is None: pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : (username, realname) couple mismatch.', http_code='401 Unauthorized') # Update entry into database cur.execute('UPDATE USERS SET SSH_KEY=(%s), SSH_KEY_HASH=(%s), STATE=2, EXPIRATION=0 \ WHERE NAME=(%s)', (pubkey, pubkey_fingerprint, username)) pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render('Update user=%s. Pending request.' % username)
def POST(self): """ Ask to sign pub key. /client username=xxxxxx => Unique username. Used by default to connect on server. [email protected] => This LDAP/AD user. # Optionnal admin_force=true|false """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') # Check if user is an admin and want to force signature when db fail force_sign = False # LDAP ADMIN authentication is_admin_auth, _ = ldap_authentification(admin=True) payload = data2map() if is_admin_auth and SERVER_OPTS['admin_db_failover'] \ and 'admin_force' in payload and payload['admin_force'].lower() == 'true': force_sign = True # Get username if 'username' in payload: username = payload['username'] else: return response_render( 'Error: No username option given. Update your CASSH >= 1.3.0', http_code='400 Bad Request') username_pattern = re_compile("^([a-z]+)$") if username_pattern.match(username) is None or username == 'all': return response_render( "Error: Username doesn't match pattern %s" \ % username_pattern.pattern, http_code='400 Bad Request') # Get realname if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') # Get public key if 'pubkey' in payload: pubkey = unquote_custom(payload['pubkey']) else: return response_render( 'Error: No pubkey given.', http_code='400 Bad Request') tmp_pubkey = NamedTemporaryFile(delete=False) tmp_pubkey.write(bytes(pubkey, 'utf-8')) tmp_pubkey.close() pubkey_fingerprint = get_fingerprint(tmp_pubkey.name) if pubkey_fingerprint == 'Unknown': remove(tmp_pubkey.name) return response_render( 'Error : Public key unprocessable', http_code='422 Unprocessable Entity') pg_conn, message = TOOLS.pg_connection() # Admin force signature case if pg_conn is None and force_sign: cert_contents = TOOLS.sign_key(tmp_pubkey.name, username, '+12h', username) remove(tmp_pubkey.name) return response_render(cert_contents, content_type='application/octet-stream') # Else, if db is down it fails. elif pg_conn is None: remove(tmp_pubkey.name) return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('SELECT * FROM USERS WHERE SSH_KEY=(%s) AND NAME=lower(%s)', (pubkey, username)) user = cur.fetchone() if user is None: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : User or Key absent, add your key again.', http_code='400 Bad Request') if username != user[0] or realname != user[1]: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : (username, realname) couple mismatch.', http_code='401 Unauthorized') status = user[2] expiry = user[6] principals = get_principals(user[7], username, shell=True) if status > 0: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render("Status: %s" % STATES[user[2]]) cert_contents = TOOLS.sign_key(tmp_pubkey.name, username, expiry, principals, db_cursor=cur) remove(tmp_pubkey.name) pg_conn.commit() cur.close() pg_conn.close() return response_render( cert_contents, content_type='application/octet-stream')
def POST(self): """ Ask to sign pub key. /client username=xxxxxx => Unique username. Used by default to connect on server. [email protected] => This LDAP/AD user. # Optionnal admin_force=true|false """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') # Check if user is an admin and want to force signature when db fail force_sign = False # LDAP ADMIN authentication is_admin_auth, _ = ldap_authentification(admin=True) payload = data2map() if is_admin_auth and SERVER_OPTS['admin_db_failover'] \ and 'admin_force' in payload and payload['admin_force'].lower() == 'true': force_sign = True # Get username if 'username' in payload: username = payload['username'] else: return response_render( 'Error: No username option given. Update your CASSH >= 1.3.0', http_code='400 Bad Request') username_pattern = re_compile("^([a-z]+)$") if username_pattern.match(username) is None or username == 'all': return response_render( "Error: Username doesn't match pattern %s" \ % username_pattern.pattern, http_code='400 Bad Request') # Get realname if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') # Get public key if 'pubkey' in payload: pubkey = unquote_custom(payload['pubkey']) else: return response_render( 'Error: No pubkey given.', http_code='400 Bad Request') tmp_pubkey = NamedTemporaryFile(delete=False) tmp_pubkey.write(bytes(pubkey, 'utf-8')) tmp_pubkey.close() pubkey_fingerprint = get_fingerprint(tmp_pubkey.name) if pubkey_fingerprint == 'Unknown': remove(tmp_pubkey.name) return response_render( 'Error : Public key unprocessable', http_code='422 Unprocessable Entity') pg_conn, message = TOOLS.pg_connection() # Admin force signature case if pg_conn is None and force_sign: cert_contents = TOOLS.sign_key(tmp_pubkey.name, username, '+12h', username) remove(tmp_pubkey.name) return response_render(cert_contents, content_type='application/octet-stream') # Else, if db is down it fails. elif pg_conn is None: remove(tmp_pubkey.name) return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('SELECT * FROM USERS WHERE SSH_KEY=(%s) AND NAME=lower(%s)', (pubkey, username)) user = cur.fetchone() if user is None: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : User or Key absent, add your key again.', http_code='400 Bad Request') if username != user[0] or realname != user[1]: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : (username, realname) couple mismatch.', http_code='401 Unauthorized') status = user[2] expiry = user[6] principals = get_principals(user[7], username, shell=True) if status > 0: cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render("Status: %s" % STATES[user[2]]) cert_contents = TOOLS.sign_key(tmp_pubkey.name, username, expiry, principals, db_cursor=cur) remove(tmp_pubkey.name) pg_conn.commit() cur.close() pg_conn.close() return response_render( cert_contents, content_type='application/octet-stream')
def GET(self): """ Return a pong """ return response_render('pong')
def GET(self): """ Return a pong """ return response_render('pong')
def PUT(self): """ This function permit to add or update a ssh public key. /client username=xxxxxx => Unique username. Used by default to connect on server. [email protected] => This LDAP/AD user. """ # LDAP authentication is_auth, message = ldap_authentification() if not is_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'username' in payload: username = payload['username'] else: return response_render( 'Error: No username option given.', http_code='400 Bad Request') username_pattern = re_compile("^([a-z]+)$") if username_pattern.match(username) is None or username == 'all': return response_render( "Error: Username doesn't match pattern %s" \ % username_pattern.pattern, http_code='400 Bad Request') if 'realname' in payload: realname = unquote_plus(payload['realname']) else: return response_render( 'Error: No realname option given.', http_code='400 Bad Request') realname_pattern = re_compile("^([a-zA-Z0-9\.]+@[a-zA-Z0-9\.]+)$") if realname_pattern.match(realname) is None: return response_render( "Error: Realname doesn't match pattern", http_code='400 Bad Request') # Get public key if 'pubkey' in payload: pubkey = unquote_custom(payload['pubkey']) else: return response_render( 'Error: No pubkey given.', http_code='400 Bad Request') tmp_pubkey = NamedTemporaryFile(delete=False) tmp_pubkey.write(bytes(pubkey, 'utf-8')) tmp_pubkey.close() pubkey_fingerprint = get_fingerprint(tmp_pubkey.name) if pubkey_fingerprint == 'Unknown': remove(tmp_pubkey.name) return response_render( 'Error : Public key unprocessable', http_code='422 Unprocessable Entity') pg_conn, message = TOOLS.pg_connection() if pg_conn is None: remove(tmp_pubkey.name) return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() # Search if key already exists cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username,)) user = cur.fetchone() # CREATE NEW USER if user is None: cur.execute('INSERT INTO USERS VALUES \ ((%s), (%s), (%s), (%s), (%s), (%s), (%s), (%s))', \ (username, realname, 2, 0, pubkey_fingerprint, pubkey, '+12h', '')) pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Create user=%s. Pending request.' % username, http_code='201 Created') else: # Check if realname is the same cur.execute('SELECT * FROM USERS WHERE NAME=(%s) AND REALNAME=lower((%s))', \ (username, realname)) if cur.fetchone() is None: pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render( 'Error : (username, realname) couple mismatch.', http_code='401 Unauthorized') # Update entry into database cur.execute('UPDATE USERS SET SSH_KEY=(%s), SSH_KEY_HASH=(%s), STATE=2, EXPIRATION=0 \ WHERE NAME=(%s)', (pubkey, pubkey_fingerprint, username)) pg_conn.commit() cur.close() pg_conn.close() remove(tmp_pubkey.name) return response_render('Update user=%s. Pending request.' % username)
def POST(self, username): """ Revoke or Active keys. /admin/<username> revoke=true/false => Revoke user status=true/false => Display status """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'revoke' in payload: do_revoke = payload['revoke'].lower() == 'true' else: do_revoke = False if 'status' in payload: do_status = payload['status'].lower() == 'true' else: do_status = False pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() if username == 'all' and do_status: return response_render( TOOLS.list_keys(), content_type='application/json') # Search if key already exists cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username,)) user = cur.fetchone() # If user dont exist if user is None: cur.close() pg_conn.close() message = 'User does not exists.' elif do_revoke: cur.execute('UPDATE USERS SET STATE=1 WHERE NAME=(%s)', (username,)) pg_conn.commit() pubkey = get_pubkey(username, pg_conn) cur.execute('INSERT INTO REVOCATION VALUES \ ((%s), (%s), (%s))', \ (pubkey, timestamp(), username)) pg_conn.commit() message = 'Revoke user=%s.' % username cur.close() pg_conn.close() # Display status elif do_status: return response_render( TOOLS.list_keys(username=username), content_type='application/json') # If user is in PENDING state elif user[2] == 2: cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() message = 'Active user=%s. SSH Key active but need to be signed.' % username # If user is in REVOKE state elif user[2] == 1: cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() message = 'Active user=%s. SSH Key active but need to be signed.' % username else: cur.close() pg_conn.close() message = 'user=%s already active. Nothing done.' % username return response_render(message)
def POST(self, username): """ Revoke or Active keys. /admin/<username> revoke=true/false => Revoke user status=true/false => Display status """ # LDAP authentication is_admin_auth, message = ldap_authentification(admin=True) if not is_admin_auth: return response_render(message, http_code='401 Unauthorized') payload = data2map() if 'revoke' in payload: do_revoke = payload['revoke'].lower() == 'true' else: do_revoke = False if 'status' in payload: do_status = payload['status'].lower() == 'true' else: do_status = False pg_conn, message = TOOLS.pg_connection() if pg_conn is None: return response_render(message, http_code='503 Service Unavailable') cur = pg_conn.cursor() if username == 'all' and do_status: return response_render( TOOLS.list_keys(), content_type='application/json') # Search if key already exists cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username,)) user = cur.fetchone() # If user dont exist if user is None: cur.close() pg_conn.close() message = 'User does not exists.' elif do_revoke: cur.execute('UPDATE USERS SET STATE=1 WHERE NAME=(%s)', (username,)) pg_conn.commit() pubkey = get_pubkey(username, pg_conn) cur.execute('INSERT INTO REVOCATION VALUES \ ((%s), (%s), (%s))', \ (pubkey, timestamp(), username)) pg_conn.commit() message = 'Revoke user=%s.' % username cur.close() pg_conn.close() # Display status elif do_status: return response_render( TOOLS.list_keys(username=username), content_type='application/json') # If user is in PENDING state elif user[2] == 2: cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() message = 'Active user=%s. SSH Key active but need to be signed.' % username # If user is in REVOKE state elif user[2] == 1: cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)', (username,)) pg_conn.commit() cur.close() pg_conn.close() message = 'Active user=%s. SSH Key active but need to be signed.' % username else: cur.close() pg_conn.close() message = 'user=%s already active. Nothing done.' % username return response_render(message)