<commit_before>/*

This file implements functionality very similar to that of the xauth module in czmq.

Notable differences in here:

 - domains are supported
 - usernames/passwords are read from memory, not from file
 - public keys are read from memory, not from file
 - additional functions for configuring server or client socket with a single command

*/

package zmq4

import (
	"errors"
	"fmt"
)

const CURVE_ALLOW_ANY = "*"

var (
	auth_handler *Socket
	auth_quit    *Socket

	auth_init    = false
	auth_verbose = false

	auth_allow = make(map[string]bool)
	auth_deny  = make(map[string]bool)

	auth_users = make(map[string]map[string]string)

	auth_pubkeys = make(map[string]map[string]bool)

	auth_meta_handler = auth_meta_handler_default
)

func auth_meta_handler_default(version, request_id, domain, address, identity, mechanism string) (metadata map[string]string) {
	return map[string]string{}
}

// SUBJECT TO CHANGE
func AuthMetaBlob(name, value string) (blob []byte, err error) {
	if len(name) > 255 {
		return []byte{}, errors.New("Name too long")
	}
	return auth_meta_blob(name, value), nil
}

func auth_meta_blob(name, value string) []byte {
	l1 := len(name)
	l2 := len(value)
	b := make([]byte, l1+l2+5)
	b[0] = byte(l1)
	b[l1+1] = byte(l2 >> 24 & 255)
	b[l1+2] = byte(l2 >> 16 & 255)
	b[l1+3] = byte(l2 >> 8 & 255)
	b[l1+4] = byte(l2 & 255)
	copy(b[1:], []byte(name))
	copy(b[5+l1:], []byte(value))
	return b
}

func auth_do_handler() {
	for {

		msg, err := auth_handler.RecvMessage(0)
		if err != nil {
			if auth_verbose {
				fmt.Println("AUTH: Terminating")
			}
			break
		}

		if msg[0] == "QUIT" {
			if auth_verbose {
				fmt.Println("AUTH: Quiting")
			}
			auth_handler.SendMessage("QUIT")
			break
		}

		version := msg[0]
		if version != "1.0" {
			panic("AUTH: version != 1.0")
		}

		request_id := msg[1]
		domain := msg[2]
		address := msg[3]
		identity := msg[4]
		mechanism := msg[5]

		username := ""
		password := ""
		client_key := ""
		if mechanism == "PLAIN" {
			username = msg[6]
			password = msg[7]
		} else if mechanism == "CURVE" {
			s := msg[6]
			if len(s) != 32 {
				panic("AUTH: len(client_key) != 32")
			}
			client_key = Z85encode(s)
		}

		allowed := false
		denied := false

		if len(auth_allow) > 0 {
			if auth_allow[address] {
				allowed = true
				if auth_verbose {
					fmt.Printf("AUTH: PASSED (whitelist) address=%q\n", address)
				}
			} else {
				denied = true
				if auth_verbose {
					fmt.Printf("AUTH: DENIED (not in whitelist) address=%q\n", address)
				}
			}
		} else if len(auth_deny) > 0 {
			if auth_deny[address] {
				denied = true
				if auth_verbose {
					fmt.Printf("AUTH: DENIED (blacklist) address=%q\n", address)
				}
			} else {
				allowed = true
				if auth_verbose {
					fmt.Printf("AUTH: PASSED (not in blacklist) address=%q\n", address)
				}
			}
		}

		// Mechanism-specific checks
		if !denied {
			if mechanism == "NULL" && !allowed {
				// For NULL, we allow if the address wasn't blacklisted
				if auth_verbose {
					fmt.Printf("AUTH: ALLOWED (NULL)\n")
				}
				allowed = true
			} else if mechanism == "PLAIN" {
				// For PLAIN, even a whitelisted address must authenticate
				allowed = authenticate_plain(domain, username, password)
			} else if mechanism == "CURVE" {
				// For CURVE, even a whitelisted address must authenticate
				allowed = authenticate_curve(domain, client_key)
			}
		}
		if allowed {
			m := auth_meta_handler(version, request_id, domain, address, identity, mechanism)
			user_id := ""
			if uid, ok := m["User-Id"]; ok {
				user_id = uid
				delete(m, "User-Id")
			}
			metadata := make([]byte, 0)
			for key, value := range m {
				if len(key) > 255 {
					continue
				}
				metadata = append(metadata, auth_meta_blob(key, value)...)
			}
			auth_handler.SendMessage(version, request_id, "200", "OK", user_id, metadata)
		} else {
			auth_handler.SendMessage(version, request_id, "400", "NO ACCESS", "", "")
		}
	}

	auth_handler.Close()
}

func authenticate_plain(domain, username, password string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_users[dom]; ok {
			if m[username] == password {
				if auth_verbose {
					fmt.Printf("AUTH: ALLOWED (PLAIN) domain=%q username=%q password=%q\n", dom, username, password)
				}
				return true
			}
		}
	}
	if auth_verbose {
		fmt.Printf("AUTH: DENIED (PLAIN) domain=%q username=%q password=%<PASSWORD>", domain, username, password)
	}
	return false
}

func authenticate_curve(domain, client_key string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_pubkeys[dom]; ok {
			if m[CURVE_ALLOW_ANY] {<commit_msg>minor change<commit_after>/*

This file implements functionality very similar to that of the xauth module in czmq.

Notable differences in here:

 - domains are supported
 - usernames/passwords are read from memory, not from file
 - public keys are read from memory, not from file
 - additional functions for configuring server or client socket with a single command

*/

package zmq4

import (
	"errors"
	"fmt"
)

const CURVE_ALLOW_ANY = "*"

var (
	auth_handler *Socket
	auth_quit    *Socket

	auth_init    = false
	auth_verbose = false

	auth_allow = make(map[string]bool)
	auth_deny  = make(map[string]bool)

	auth_users = make(map[string]map[string]string)

	auth_pubkeys = make(map[string]map[string]bool)

	auth_meta_handler = auth_meta_handler_default
)

func auth_meta_handler_default(version, request_id, domain, address, identity, mechanism string) (metadata map[string]string) {
	return map[string]string{}
}

// SUBJECT TO CHANGE
func AuthMetaBlob(name, value string) (blob []byte, err error) {
	if len(name) > 255 {
		return []byte{}, errors.New("Name too long")
	}
	return auth_meta_blob(name, value), nil
}

func auth_meta_blob(name, value string) []byte {
	l1 := len(name)
	l2 := len(value)
	b := make([]byte, l1+l2+5)
	b[0] = byte(l1)
	b[l1+1] = byte(l2 >> 24 & 255)
	b[l1+2] = byte(l2 >> 16 & 255)
	b[l1+3] = byte(l2 >> 8 & 255)
	b[l1+4] = byte(l2 & 255)
	copy(b[1:], []byte(name))
	copy(b[5+l1:], []byte(value))
	return b
}

func auth_do_handler() {
	for {

		msg, err := auth_handler.RecvMessage(0)
		if err != nil {
			if auth_verbose {
				fmt.Println("AUTH: Terminating")
			}
			break
		}

		if msg[0] == "QUIT" {
			if auth_verbose {
				fmt.Println("AUTH: Quiting")
			}
			auth_handler.SendMessage("QUIT")
			break
		}

		version := msg[0]
		if version != "1.0" {
			panic("AUTH: version != 1.0")
		}

		request_id := msg[1]
		domain := msg[2]
		address := msg[3]
		identity := msg[4]
		mechanism := msg[5]

		username := ""
		password := ""
		client_key := ""
		if mechanism == "PLAIN" {
			username = msg[6]
			password = msg[7]
		} else if mechanism == "CURVE" {
			s := msg[6]
			if len(s) != 32 {
				panic("AUTH: len(client_key) != 32")
			}
			client_key = Z85encode(s)
		}

		allowed := false
		denied := false

		if len(auth_allow) > 0 {
			if auth_allow[address] {
				allowed = true
				if auth_verbose {
					fmt.Printf("AUTH: PASSED (whitelist) address=%q\n", address)
				}
			} else {
				denied = true
				if auth_verbose {
					fmt.Printf("AUTH: DENIED (not in whitelist) address=%q\n", address)
				}
			}
		} else if len(auth_deny) > 0 {
			if auth_deny[address] {
				denied = true
				if auth_verbose {
					fmt.Printf("AUTH: DENIED (blacklist) address=%q\n", address)
				}
			} else {
				allowed = true
				if auth_verbose {
					fmt.Printf("AUTH: PASSED (not in blacklist) address=%q\n", address)
				}
			}
		}

		// Mechanism-specific checks
		if !denied {
			if mechanism == "NULL" && !allowed {
				// For NULL, we allow if the address wasn't blacklisted
				if auth_verbose {
					fmt.Printf("AUTH: ALLOWED (NULL)\n")
				}
				allowed = true
			} else if mechanism == "PLAIN" {
				// For PLAIN, even a whitelisted address must authenticate
				allowed = authenticate_plain(domain, username, password)
			} else if mechanism == "CURVE" {
				// For CURVE, even a whitelisted address must authenticate
				allowed = authenticate_curve(domain, client_key)
			}
		}
		if allowed {
			m := auth_meta_handler(version, request_id, domain, address, identity, mechanism)
			user_id := ""
			if uid, ok := m["User-Id"]; ok {
				user_id = uid
				delete(m, "User-Id")
			}
			metadata := make([]byte, 0)
			for key, value := range m {
				if len(key) < 256 {
					metadata = append(metadata, auth_meta_blob(key, value)...)
				}
			}
			auth_handler.SendMessage(version, request_id, "200", "OK", user_id, metadata)
		} else {
			auth_handler.SendMessage(version, request_id, "400", "NO ACCESS", "", "")
		}
	}

	auth_handler.Close()
}

func authenticate_plain(domain, username, password string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_users[dom]; ok {
			if m[username] == password {
				if auth_verbose {
					fmt.Printf("AUTH: ALLOWED (PLAIN) domain=%q username=%q password=%q\n", dom, username, password)
				}
				return true
			}
		}
	}
	if auth_verbose {
		fmt.Printf("AUTH: DENIED (PLAIN) domain=%q username=%q password=%q\n", domain, username, password)
	}
	return false
}

func authenticate_curve(domain, client_key string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_pubkeys[dom]; ok {
			if m[CURVE_ALLOW_ANY] {
{'fraction_non_alphanumeric': 0.14254929425708437, 'fraction_numerical': 0.016377545523111735, 'mean_word_length': 3.3333333333333335, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6279285', 'n_tokens_mistral': 3913, 'n_tokens_neox': 3330, 'n_words': 864}