From 3a278525ea8243c6073099490d23d079eb5fa255 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 16 Jun 2021 19:34:07 -0500 Subject: [PATCH] Create or reset SIP account New command to create or reset SIP account. Always try create first because it's faster and more common, fall back to search the list for our account if that fails due to conflict. Password is always randomly generated from the mnemonicode word list. --- config.dhall.sample | 4 +- lib/catapult.rb | 52 ++++++ lib/customer.rb | 11 ++ lib/mn_words.rb | 277 +++++++++++++++++++++++++++++ lib/sip_account.rb | 101 +++++++++++ sgx_jmp.rb | 20 +++ test/data/catapult_create_sip.json | 1 + test/test_customer.rb | 77 ++++++++ test/test_helper.rb | 2 + test/test_sip_account.rb | 121 +++++++++++++ 10 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 lib/mn_words.rb create mode 100644 lib/sip_account.rb create mode 100644 test/data/catapult_create_sip.json create mode 100644 test/test_sip_account.rb diff --git a/config.dhall.sample b/config.dhall.sample index c050569..033a982 100644 --- a/config.dhall.sample +++ b/config.dhall.sample @@ -17,7 +17,9 @@ user = "", token = "", secret = "", - application_id = "" + application_id = "", + domain = "", + sip_host = "" }, web_register = { to = "cheogram", diff --git a/lib/catapult.rb b/lib/catapult.rb index 4bed9ac..a3d11c4 100644 --- a/lib/catapult.rb +++ b/lib/catapult.rb @@ -8,6 +8,8 @@ class Catapult token String secret String application_id String + domain String + sip_host String end def import(body) @@ -17,6 +19,44 @@ class Catapult ) end + def create_endpoint(body) + post( + "domains/#{@domain}/endpoints", + body: { applicationId: @application_id }.merge(body) + ).then do |http| + unless http.response_header.status == 201 + raise "Create new SIP account failed" + end + http.response_header["location"] + end + end + + def endpoint_list(page=0) + get( + "domains/#{@domain}/endpoints", + query: { size: 1000, page: page } + ).then do |http| + next [] if http.response_header.status == 404 + raise "Could not list endpoints" if http.response_header.status != 200 + + JSON.parse(http.response) + end + end + + def endpoint_find(name, page=0) + endpoint_list(page).then do |list| + next if list.empty? + + if (found = list.find { |e| e["name"] == name }) + found.merge("url" => CATAPULT.mkurl( + "domains/#{found['domainId']}/endpoints/#{found['id']}" + )) + else + endpoint_find(name, page + 1) + end + end + end + def post(path, body:, head: {}) EM::HttpRequest.new( mkurl(path), tls: { verify_peer: true } @@ -26,6 +66,18 @@ class Catapult ) end + def delete(path, head: {}) + EM::HttpRequest.new( + mkurl(path), tls: { verify_peer: true } + ).adelete(head: catapult_headers.merge(head)) + end + + def get(path, head: {}, **kwargs) + EM::HttpRequest.new( + mkurl(path), tls: { verify_peer: true } + ).aget(head: catapult_headers.merge(head), **kwargs) + end + def mkurl(path) base = "https://api.catapult.inetwork.com/v1/users/#{@user}/" return path if path.start_with?(base) diff --git a/lib/customer.rb b/lib/customer.rb index 6bf09b4..03ff4ab 100644 --- a/lib/customer.rb +++ b/lib/customer.rb @@ -9,6 +9,7 @@ require_relative "./backend_sgx" require_relative "./ibr" require_relative "./payment_methods" require_relative "./plan" +require_relative "./sip_account" class Customer def self.for_jid(jid) @@ -103,5 +104,15 @@ class Customer BLATHER << @sgx.stanza(stanza) end + def sip_account + SipAccount.find(customer_id) + end + + def reset_sip_account + SipAccount::New.new(username: customer_id).put.catch do + sip_account.then { |acct| acct.with_random_password.put } + end + end + protected def_delegator :@plan, :expires_at end diff --git a/lib/mn_words.rb b/lib/mn_words.rb new file mode 100644 index 0000000..8d69c4f --- /dev/null +++ b/lib/mn_words.rb @@ -0,0 +1,277 @@ +# frozen_string_literal: true + +MN_WORDS = [ + "academy", "acrobat", "active", "actor", "adam", "admiral", + "adrian", "africa", "agenda", "agent", "airline", "airport", + "aladdin", "alarm", "alaska", "albert", "albino", "album", + "alcohol", "alex", "algebra", "alibi", "alice", "alien", + "alpha", "alpine", "amadeus", "amanda", "amazon", "amber", + "america", "amigo", "analog", "anatomy", "angel", "animal", + "antenna", "antonio", "apollo", "april", "archive", "arctic", + "arizona", "arnold", "aroma", "arthur", "artist", "asia", + "aspect", "aspirin", "athena", "athlete", "atlas", "audio", + "august", "austria", "axiom", "aztec", "balance", "ballad", + "banana", "bandit", "banjo", "barcode", "baron", "basic", + "battery", "belgium", "berlin", "bermuda", "bernard", "bikini", + "binary", "bingo", "biology", "block", "blonde", "bonus", + "boris", "boston", "boxer", "brandy", "bravo", "brazil", + "bronze", "brown", "bruce", "bruno", "burger", "burma", + "cabinet", "cactus", "cafe", "cairo", "cake", "calypso", + "camel", "camera", "campus", "canada", "canal", "cannon", + "canoe", "cantina", "canvas", "canyon", "capital", "caramel", + "caravan", "carbon", "cargo", "carlo", "carol", "carpet", + "cartel", "casino", "castle", "castro", "catalog", "caviar", + "cecilia", "cement", "center", "century", "ceramic", "chamber", + "chance", "change", "chaos", "charlie", "charm", "charter", + "chef", "chemist", "cherry", "chess", "chicago", "chicken", + "chief", "china", "cigar", "cinema", "circus", "citizen", + "city", "clara", "classic", "claudia", "clean", "client", + "climax", "clinic", "clock", "club", "cobra", "coconut", + "cola", "collect", "colombo", "colony", "color", "combat", + "comedy", "comet", "command", "compact", "company", "complex", + "concept", "concert", "connect", "consul", "contact", "context", + "contour", "control", "convert", "copy", "corner", "corona", + "correct", "cosmos", "couple", "courage", "cowboy", "craft", + "crash", "credit", "cricket", "critic", "crown", "crystal", + "cuba", "culture", "dallas", "dance", "daniel", "david", + "decade", "decimal", "deliver", "delta", "deluxe", "demand", + "demo", "denmark", "derby", "design", "detect", "develop", + "diagram", "dialog", "diamond", "diana", "diego", "diesel", + "diet", "digital", "dilemma", "diploma", "direct", "disco", + "disney", "distant", "doctor", "dollar", "dominic", "domino", + "donald", "dragon", "drama", "dublin", "duet", "dynamic", + "east", "ecology", "economy", "edgar", "egypt", "elastic", + "elegant", "element", "elite", "elvis", "email", "energy", + "engine", "english", "episode", "equator", "escort", "ethnic", + "europe", "everest", "evident", "exact", "example", "exit", + "exotic", "export", "express", "extra", "fabric", "factor", + "falcon", "family", "fantasy", "fashion", "fiber", "fiction", + "fidel", "fiesta", "figure", "film", "filter", "final", + "finance", "finish", "finland", "flash", "florida", "flower", + "fluid", "flute", "focus", "ford", "forest", "formal", + "format", "formula", "fortune", "forum", "fragile", "france", + "frank", "friend", "frozen", "future", "gabriel", "galaxy", + "gallery", "gamma", "garage", "garden", "garlic", "gemini", + "general", "genetic", "genius", "germany", "global", "gloria", + "golf", "gondola", "gong", "good", "gordon", "gorilla", + "grand", "granite", "graph", "green", "group", "guide", + "guitar", "guru", "hand", "happy", "harbor", "harmony", + "harvard", "havana", "hawaii", "helena", "hello", "henry", + "hilton", "history", "horizon", "hotel", "human", "humor", + "icon", "idea", "igloo", "igor", "image", "impact", + "import", "index", "india", "indigo", "input", "insect", + "instant", "iris", "italian", "jacket", "jacob", "jaguar", + "janet", "japan", "jargon", "jazz", "jeep", "john", + "joker", "jordan", "jumbo", "june", "jungle", "junior", + "jupiter", "karate", "karma", "kayak", "kermit", "kilo", + "king", "koala", "korea", "labor", "lady", "lagoon", + "laptop", "laser", "latin", "lava", "lecture", "left", + "legal", "lemon", "level", "lexicon", "liberal", "libra", + "limbo", "limit", "linda", "linear", "lion", "liquid", + "liter", "little", "llama", "lobby", "lobster", "local", + "logic", "logo", "lola", "london", "lotus", "lucas", + "lunar", "machine", "macro", "madam", "madonna", "madrid", + "maestro", "magic", "magnet", "magnum", "major", "mama", + "mambo", "manager", "mango", "manila", "marco", "marina", + "market", "mars", "martin", "marvin", "master", "matrix", + "maximum", "media", "medical", "mega", "melody", "melon", + "memo", "mental", "mentor", "menu", "mercury", "message", + "metal", "meteor", "meter", "method", "metro", "mexico", + "miami", "micro", "million", "mineral", "minimum", "minus", + "minute", "miracle", "mirage", "miranda", "mister", "mixer", + "mobile", "model", "modem", "modern", "modular", "moment", + "monaco", "monica", "monitor", "mono", "monster", "montana", + "morgan", "motel", "motif", "motor", "mozart", "multi", + "museum", "music", "mustang", "natural", "neon", "nepal", + "neptune", "nerve", "neutral", "nevada", "news", "ninja", + "nirvana", "normal", "nova", "novel", "nuclear", "numeric", + "nylon", "oasis", "object", "observe", "ocean", "octopus", + "olivia", "olympic", "omega", "opera", "optic", "optimal", + "orange", "orbit", "organic", "orient", "origin", "orlando", + "oscar", "oxford", "oxygen", "ozone", "pablo", "pacific", + "pagoda", "palace", "pamela", "panama", "panda", "panel", + "panic", "paradox", "pardon", "paris", "parker", "parking", + "parody", "partner", "passage", "passive", "pasta", "pastel", + "patent", "patriot", "patrol", "patron", "pegasus", "pelican", + "penguin", "pepper", "percent", "perfect", "perfume", "period", + "permit", "person", "peru", "phone", "photo", "piano", + "picasso", "picnic", "picture", "pigment", "pilgrim", "pilot", + "pirate", "pixel", "pizza", "planet", "plasma", "plaster", + "plastic", "plaza", "pocket", "poem", "poetic", "poker", + "polaris", "police", "politic", "polo", "polygon", "pony", + "popcorn", "popular", "postage", "postal", "precise", "prefix", + "premium", "present", "price", "prince", "printer", "prism", + "private", "product", "profile", "program", "project", "protect", + "proton", "public", "pulse", "puma", "pyramid", "queen", + "radar", "radio", "random", "rapid", "rebel", "record", + "recycle", "reflex", "reform", "regard", "regular", "relax", + "report", "reptile", "reverse", "ricardo", "ringo", "ritual", + "robert", "robot", "rocket", "rodeo", "romeo", "royal", + "russian", "safari", "salad", "salami", "salmon", "salon", + "salute", "samba", "sandra", "santana", "sardine", "school", + "screen", "script", "second", "secret", "section", "segment", + "select", "seminar", "senator", "senior", "sensor", "serial", + "service", "sheriff", "shock", "sierra", "signal", "silicon", + "silver", "similar", "simon", "single", "siren", "slogan", + "social", "soda", "solar", "solid", "solo", "sonic", + "soviet", "special", "speed", "spiral", "spirit", "sport", + "static", "station", "status", "stereo", "stone", "stop", + "street", "strong", "student", "studio", "style", "subject", + "sultan", "super", "susan", "sushi", "suzuki", "switch", + "symbol", "system", "tactic", "tahiti", "talent", "tango", + "tarzan", "taxi", "telex", "tempo", "tennis", "texas", + "textile", "theory", "thermos", "tiger", "titanic", "tokyo", + "tomato", "topic", "tornado", "toronto", "torpedo", "total", + "totem", "tourist", "tractor", "traffic", "transit", "trapeze", + "travel", "tribal", "trick", "trident", "trilogy", "tripod", + "tropic", "trumpet", "tulip", "tuna", "turbo", "twist", + "ultra", "uniform", "union", "uranium", "vacuum", "valid", + "vampire", "vanilla", "vatican", "velvet", "ventura", "venus", + "vertigo", "veteran", "victor", "video", "vienna", "viking", + "village", "vincent", "violet", "violin", "virtual", "virus", + "visa", "vision", "visitor", "visual", "vitamin", "viva", + "vocal", "vodka", "volcano", "voltage", "volume", "voyage", + "water", "weekend", "welcome", "western", "window", "winter", + "wizard", "wolf", "world", "xray", "yankee", "yoga", + "yogurt", "yoyo", "zebra", "zero", "zigzag", "zipper", + "zodiac", "zoom", "abraham", "action", "address", "alabama", + "alfred", "almond", "ammonia", "analyze", "annual", "answer", + "apple", "arena", "armada", "arsenal", "atlanta", "atomic", + "avenue", "average", "bagel", "baker", "ballet", "bambino", + "bamboo", "barbara", "basket", "bazaar", "benefit", "bicycle", + "bishop", "blitz", "bonjour", "bottle", "bridge", "british", + "brother", "brush", "budget", "cabaret", "cadet", "candle", + "capitan", "capsule", "career", "cartoon", "channel", "chapter", + "cheese", "circle", "cobalt", "cockpit", "college", "compass", + "comrade", "condor", "crimson", "cyclone", "darwin", "declare", + "degree", "delete", "delphi", "denver", "desert", "divide", + "dolby", "domain", "domingo", "double", "drink", "driver", + "eagle", "earth", "echo", "eclipse", "editor", "educate", + "edward", "effect", "electra", "emerald", "emotion", "empire", + "empty", "escape", "eternal", "evening", "exhibit", "expand", + "explore", "extreme", "ferrari", "first", "flag", "folio", + "forget", "forward", "freedom", "fresh", "friday", "fuji", + "galileo", "garcia", "genesis", "gold", "gravity", "habitat", + "hamlet", "harlem", "helium", "holiday", "house", "hunter", + "ibiza", "iceberg", "imagine", "infant", "isotope", "jackson", + "jamaica", "jasmine", "java", "jessica", "judo", "kitchen", + "lazarus", "letter", "license", "lithium", "loyal", "lucky", + "magenta", "mailbox", "manual", "marble", "mary", "maxwell", + "mayor", "milk", "monarch", "monday", "money", "morning", + "mother", "mystery", "native", "nectar", "nelson", "network", + "next", "nikita", "nobel", "nobody", "nominal", "norway", + "nothing", "number", "october", "office", "oliver", "opinion", + "option", "order", "outside", "package", "pancake", "pandora", + "panther", "papa", "patient", "pattern", "pedro", "pencil", + "people", "phantom", "philips", "pioneer", "pluto", "podium", + "portal", "potato", "prize", "process", "protein", "proxy", + "pump", "pupil", "python", "quality", "quarter", "quiet", + "rabbit", "radical", "radius", "rainbow", "ralph", "ramirez", + "ravioli", "raymond", "respect", "respond", "result", "resume", + "retro", "richard", "right", "risk", "river", "roger", + "roman", "rondo", "sabrina", "salary", "salsa", "sample", + "samuel", "saturn", "savage", "scarlet", "scoop", "scorpio", + "scratch", "scroll", "sector", "serpent", "shadow", "shampoo", + "sharon", "sharp", "short", "shrink", "silence", "silk", + "simple", "slang", "smart", "smoke", "snake", "society", + "sonar", "sonata", "soprano", "source", "sparta", "sphere", + "spider", "sponsor", "spring", "acid", "adios", "agatha", + "alamo", "alert", "almanac", "aloha", "andrea", "anita", + "arcade", "aurora", "avalon", "baby", "baggage", "balloon", + "bank", "basil", "begin", "biscuit", "blue", "bombay", + "brain", "brenda", "brigade", "cable", "carmen", "cello", + "celtic", "chariot", "chrome", "citrus", "civil", "cloud", + "common", "compare", "cool", "copper", "coral", "crater", + "cubic", "cupid", "cycle", "depend", "door", "dream", + "dynasty", "edison", "edition", "enigma", "equal", "eric", + "event", "evita", "exodus", "extend", "famous", "farmer", + "food", "fossil", "frog", "fruit", "geneva", "gentle", + "george", "giant", "gilbert", "gossip", "gram", "greek", + "grille", "hammer", "harvest", "hazard", "heaven", "herbert", + "heroic", "hexagon", "husband", "immune", "inca", "inch", + "initial", "isabel", "ivory", "jason", "jerome", "joel", + "joshua", "journal", "judge", "juliet", "jump", "justice", + "kimono", "kinetic", "leonid", "lima", "maze", "medusa", + "member", "memphis", "michael", "miguel", "milan", "mile", + "miller", "mimic", "mimosa", "mission", "monkey", "moral", + "moses", "mouse", "nancy", "natasha", "nebula", "nickel", + "nina", "noise", "orchid", "oregano", "origami", "orinoco", + "orion", "othello", "paper", "paprika", "prelude", "prepare", + "pretend", "profit", "promise", "provide", "puzzle", "remote", + "repair", "reply", "rival", "riviera", "robin", "rose", + "rover", "rudolf", "saga", "sahara", "scholar", "shelter", + "ship", "shoe", "sigma", "sister", "sleep", "smile", + "spain", "spark", "split", "spray", "square", "stadium", + "star", "storm", "story", "strange", "stretch", "stuart", + "subway", "sugar", "sulfur", "summer", "survive", "sweet", + "swim", "table", "taboo", "target", "teacher", "telecom", + "temple", "tibet", "ticket", "tina", "today", "toga", + "tommy", "tower", "trivial", "tunnel", "turtle", "twin", + "uncle", "unicorn", "unique", "update", "valery", "vega", + "version", "voodoo", "warning", "william", "wonder", "year", + "yellow", "young", "absent", "absorb", "accent", "alfonso", + "alias", "ambient", "andy", "anvil", "appear", "apropos", + "archer", "ariel", "armor", "arrow", "austin", "avatar", + "axis", "baboon", "bahama", "bali", "balsa", "bazooka", + "beach", "beast", "beatles", "beauty", "before", "benny", + "betty", "between", "beyond", "billy", "bison", "blast", + "bless", "bogart", "bonanza", "book", "border", "brave", + "bread", "break", "broken", "bucket", "buenos", "buffalo", + "bundle", "button", "buzzer", "byte", "caesar", "camilla", + "canary", "candid", "carrot", "cave", "chant", "child", + "choice", "chris", "cipher", "clarion", "clark", "clever", + "cliff", "clone", "conan", "conduct", "congo", "content", + "costume", "cotton", "cover", "crack", "current", "danube", + "data", "decide", "desire", "detail", "dexter", "dinner", + "dispute", "donor", "druid", "drum", "easy", "eddie", + "enjoy", "enrico", "epoxy", "erosion", "except", "exile", + "explain", "fame", "fast", "father", "felix", "field", + "fiona", "fire", "fish", "flame", "flex", "flipper", + "float", "flood", "floor", "forbid", "forever", "fractal", + "frame", "freddie", "front", "fuel", "gallop", "game", + "garbo", "gate", "gibson", "ginger", "giraffe", "gizmo", + "glass", "goblin", "gopher", "grace", "gray", "gregory", + "grid", "griffin", "ground", "guest", "gustav", "gyro", + "hair", "halt", "harris", "heart", "heavy", "herman", + "hippie", "hobby", "honey", "hope", "horse", "hostel", + "hydro", "imitate", "info", "ingrid", "inside", "invent", + "invest", "invite", "iron", "ivan", "james", "jester", + "jimmy", "join", "joseph", "juice", "julius", "july", + "justin", "kansas", "karl", "kevin", "kiwi", "ladder", + "lake", "laura", "learn", "legacy", "legend", "lesson", + "life", "light", "list", "locate", "lopez", "lorenzo", + "love", "lunch", "malta", "mammal", "margo", "marion", + "mask", "match", "mayday", "meaning", "mercy", "middle", + "mike", "mirror", "modest", "morph", "morris", "nadia", + "nato", "navy", "needle", "neuron", "never", "newton", + "nice", "night", "nissan", "nitro", "nixon", "north", + "oberon", "octavia", "ohio", "olga", "open", "opus", + "orca", "oval", "owner", "page", "paint", "palma", + "parade", "parent", "parole", "paul", "peace", "pearl", + "perform", "phoenix", "phrase", "pierre", "pinball", "place", + "plate", "plato", "plume", "pogo", "point", "polite", + "polka", "poncho", "powder", "prague", "press", "presto", + "pretty", "prime", "promo", "quasi", "quest", "quick", + "quiz", "quota", "race", "rachel", "raja", "ranger", + "region", "remark", "rent", "reward", "rhino", "ribbon", + "rider", "road", "rodent", "round", "rubber", "ruby", + "rufus", "sabine", "saddle", "sailor", "saint", "salt", + "satire", "scale", "scuba", "season", "secure", "shake", + "shallow", "shannon", "shave", "shelf", "sherman", "shine", + "shirt", "side", "sinatra", "sincere", "size", "slalom", + "slow", "small", "snow", "sofia", "song", "sound", + "south", "speech", "spell", "spend", "spoon", "stage", + "stamp", "stand", "state", "stella", "stick", "sting", + "stock", "store", "sunday", "sunset", "support", "sweden", + "swing", "tape", "think", "thomas", "tictac", "time", + "toast", "tobacco", "tonight", "torch", "torso", "touch", + "toyota", "trade", "tribune", "trinity", "triton", "truck", + "trust", "type", "under", "unit", "urban", "urgent", + "user", "value", "vendor", "venice", "verona", "vibrate", + "virgo", "visible", "vista", "vital", "voice", "vortex", + "waiter", "watch", "wave", "weather", "wedding", "wheel", + "whiskey", "wisdom", "deal", "null", "nurse", "quebec", + "reserve", "reunion", "roof", "singer", "verbal", "amen", + "ego", "fax", "jet", "job", "rio", "ski", + "yes" +].freeze diff --git a/lib/sip_account.rb b/lib/sip_account.rb new file mode 100644 index 0000000..7f82f62 --- /dev/null +++ b/lib/sip_account.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require "securerandom" +require "value_semantics/monkey_patched" + +require_relative "./catapult" +require_relative "./mn_words" + +class SipAccount + def self.find(name) + CATAPULT.endpoint_find(name).then do |found| + next New.new(username: name) unless found + + new(username: found["name"], url: found["url"]) + end + end + + module Common + def with_random_password + with(password: MN_WORDS.sample(3).join(" ")) + end + + protected + + def create + CATAPULT.create_endpoint( + name: username, + credentials: { password: password } + ).then do |url| + with(url: url) + end + end + end + + include Common + + value_semantics do + url String + username String + password Either(String, nil), default: nil + end + + def form + form = Blather::Stanza::X.new(:result) + form.title = "Sip Account Reset!" + form.instructions = "These are your new SIP credentials" + + form.fields = [ + { var: "username", value: username, label: "Username" }, + { var: "password", value: password, label: "Password" }, + { var: "server", value: server, label: "Server" } + ] + + form + end + + def put + delete.then { create } + end + + def delete + CATAPULT.delete(url).then do |http| + unless http.response_header.status == 200 + raise "Delete old SIP account failed" + end + + self + end + end + +protected + + protected :url, :username, :password + + def server + CATAPULT.sip_host + end + + class New + include Common + + value_semantics do + username String + password String, default_generator: -> { MN_WORDS.sample(3).join(" ") } + end + + def put + create + end + + def with(**kwargs) + if kwargs.key?(:url) + SipAccount.new(internal_to_h.merge(kwargs)) + else + super + end + end + + protected :username, :password + end +end diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 642429f..1edcded 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -258,6 +258,11 @@ disco_items node: "http://jabber.org/protocol/commands" do |iq| iq.to, "usage", "Show Monthly Usage" + ), + Blather::Stanza::DiscoItems::Item.new( + iq.to, + "reset sip account", + "Create or Reset SIP Account" ) ] self << reply @@ -321,6 +326,21 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq| }.catch { |e| panic(e, sentry_hub) } end +command :execute?, node: "reset sip account", sessionid: nil do |iq| + sentry_hub = new_sentry_hub(iq, name: iq.node) + Customer.for_jid(iq.from.stripped).then { |customer| + sentry_hub.current_scope.set_user( + id: customer.customer_id, + jid: iq.from.stripped.to_s + ) + customer.reset_sip_account + }.then { |sip_account| + reply = iq.reply + reply.command << sip_account.form + BLATHER << reply + }.catch { |e| panic(e, sentry_hub) } +end + command :execute?, node: "usage", sessionid: nil do |iq| sentry_hub = new_sentry_hub(iq, name: iq.node) report_for = (Date.today..(Date.today << 1)) diff --git a/test/data/catapult_create_sip.json b/test/data/catapult_create_sip.json new file mode 100644 index 0000000..64fed7d --- /dev/null +++ b/test/data/catapult_create_sip.json @@ -0,0 +1 @@ +{"applicationId":"catapult_app","name":"12345","credentials":{"password":"old password"}} diff --git a/test/test_customer.rb b/test/test_customer.rb index e391084..2ebb9de 100644 --- a/test/test_customer.rb +++ b/test/test_customer.rb @@ -11,6 +11,14 @@ CustomerPlan::DB = Minitest::Mock.new CustomerUsage::REDIS = Minitest::Mock.new CustomerUsage::DB = Minitest::Mock.new +class SipAccount + public :username, :url + + class New + public :username + end +end + class CustomerTest < Minitest::Test def test_for_jid Customer::REDIS.expect( @@ -205,4 +213,73 @@ class CustomerTest < Minitest::Test ) end em :test_customer_usage_report + + def test_sip_account_new + req = stub_request( + :get, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints?page=0&size=1000" + ).with( + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0" + } + ).to_return(status: 404) + sip = Customer.new("test").sip_account.sync + assert_kind_of SipAccount::New, sip + assert_equal "test", sip.username + assert_requested req + end + em :test_sip_account_new + + def test_sip_account_existing + req1 = stub_request( + :get, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints?page=0&size=1000" + ).with( + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0" + } + ).to_return(status: 200, body: [ + { name: "NOTtest", domainId: "domain", id: "endpoint" } + ].to_json) + + req2 = stub_request( + :get, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints?page=1&size=1000" + ).with( + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0" + } + ).to_return(status: 200, body: [ + { name: "test", domainId: "domain", id: "endpoint" } + ].to_json) + + sip = Customer.new("test").sip_account.sync + assert_kind_of SipAccount, sip + assert_equal "test", sip.username + assert_equal( + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/domain/endpoints/endpoint", + sip.url + ) + + assert_requested req1 + assert_requested req2 + end + em :test_sip_account_existing + + def test_sip_account_error + stub_request( + :get, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints?page=0&size=1000" + ).to_return(status: 400) + + assert_raises(RuntimeError) do + Customer.new("test").sip_account.sync + end + end + em :test_sip_account_error end diff --git a/test/test_helper.rb b/test/test_helper.rb index 3978650..98ed484 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -48,6 +48,8 @@ CONFIG = { user: "catapult_user", token: "catapult_token", secret: "catapult_secret", + domain: "catapult_domain", + sip_host: "host.bwapp.io.example.com", application_id: "catapult_app" }, activation_amount: 1, diff --git a/test/test_sip_account.rb b/test/test_sip_account.rb new file mode 100644 index 0000000..3ced37c --- /dev/null +++ b/test/test_sip_account.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require "test_helper" +require "sip_account" + +class SipAccount + public :password, :url + + class New + public :password + end +end + +class SipAccountTest < Minitest::Test + def setup + @sip = SipAccount.new( + url: "https://api.catapult.inetwork.com/v1/" \ + "users/catapult_user/domains/catapult_domain/endpoints/test", + username: "12345", + password: "old password" + ) + end + + def test_with_random_password + new_sip = @sip.with_random_password + refute_equal @sip.password, new_sip.password + refute_empty new_sip.password + assert_kind_of String, new_sip.password + end + + def test_form + form = @sip.form + assert_equal "12345", form.field("username").value + assert_equal "old password", form.field("password").value + assert_equal "host.bwapp.io.example.com", form.field("server").value + end + + def test_put + delete = stub_request(:delete, @sip.url).with( + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0" + } + ).to_return(status: 200) + + post = stub_request( + :post, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints" + ).with( + body: open(__dir__ + "/data/catapult_create_sip.json").read.chomp, + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0", + "Content-Type" => "application/json" + } + ).to_return( + status: 201, + headers: { "Location" => "http://example.com/endpoint" } + ) + + new_sip = @sip.put.sync + assert_equal "http://example.com/endpoint", new_sip.url + assert_requested delete + assert_requested post + end + em :test_put + + def test_put_delete_fail + stub_request(:delete, @sip.url).to_return(status: 400) + assert_raises(RuntimeError) { @sip.put.sync } + end + em :test_put_delete_fail + + def test_put_post_fail + stub_request(:delete, @sip.url).to_return(status: 200) + stub_request( + :post, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints" + ).to_return(status: 400) + assert_raises(RuntimeError) { @sip.put.sync } + end + em :test_put_post_fail + + class NewTest < Minitest::Test + def setup + @sip = SipAccount::New.new( + username: "12345", + password: "old password" + ) + end + + def test_with_random_password + new_sip = @sip.with_random_password + refute_equal @sip.password, new_sip.password + refute_empty new_sip.password + assert_kind_of String, new_sip.password + end + + def test_put + post = stub_request( + :post, + "https://api.catapult.inetwork.com/v1/users/" \ + "catapult_user/domains/catapult_domain/endpoints" + ).with( + body: open(__dir__ + "/data/catapult_create_sip.json").read.chomp, + headers: { + "Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0", + "Content-Type" => "application/json" + } + ).to_return( + status: 201, + headers: { "Location" => "http://example.com/endpoint" } + ) + + new_sip = @sip.put.sync + assert_equal "http://example.com/endpoint", new_sip.url + assert_requested post + end + em :test_put + end +end -- 2.38.4