diff --git a/lib/tasks/anonymize.rake b/lib/tasks/anonymize.rake new file mode 100755 index 0000000000..b42b4aea5e --- /dev/null +++ b/lib/tasks/anonymize.rake @@ -0,0 +1,250 @@ +desc "Anonymize your database -- DON'T USE THIS UNLESS YOU REALLY, REALLY KNOW WHAT YOU'RE DOING. NOT KIDDING HERE!" + +$LOREM = " +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer in +mi a mauris ornare sagittis. Suspendisse potenti. Suspendisse dapibus +dignissim dolor. Nam sapien tellus, tempus et, tempus ac, tincidunt +in, arcu. Duis dictum. Proin magna nulla, pellentesque non, commodo +et, iaculis sit amet, mi. Mauris condimentum massa ut metus. Donec +viverra, sapien mattis rutrum tristique, lacus eros semper tellus, et +molestie nisi sapien eu massa. Vestibulum ante ipsum primis in +faucibus orci luctus et ultrices posuere cubilia Curae; Fusce erat +tortor, mollis ut, accumsan ut, lacinia gravida, libero. Curabitur +massa felis, accumsan feugiat, convallis sit amet, porta vel, neque. +Duis et ligula non elit ultricies rutrum. Suspendisse tempor. +Quisque posuere malesuada velit. Sed pellentesque mi a purus. Integer +imperdiet, orci a eleifend mollis, velit nulla iaculis arcu, eu rutrum +magna quam sed elit. Nullam egestas. Integer interdum purus nec +mauris. Vestibulum ac mi in nunc suscipit dapibus. Duis consectetuer, +ipsum et pharetra sollicitudin, metus turpis facilisis magna, vitae +dictum ligula nulla nec mi. Nunc ante urna, gravida sit amet, congue +et, accumsan vitae, magna. Praesent luctus. Nullam in velit. Praesent +est. Curabitur turpis. +Class aptent taciti sociosqu ad litora torquent per conubia nostra, +per inceptos hymenaeos. Cras consectetuer, nibh in lacinia ornare, +turpis sem tempor massa, sagittis feugiat mauris nibh non tellus. +Phasellus mi. Fusce enim. Mauris ultrices, turpis eu adipiscing +viverra, justo libero ullamcorper massa, id ultrices velit est quis +tortor. Quisque condimentum, lacus volutpat nonummy accumsan, est nunc +imperdiet magna, vulputate aliquet nisi risus at est. Aliquam +imperdiet gravida tortor. Praesent interdum accumsan ante. Vivamus est +ligula, consequat sed, pulvinar eu, consequat vitae, eros. Nulla elit +nunc, congue eget, scelerisque a, tempor ac, nisi. Morbi facilisis. +Pellentesque habitant morbi tristique senectus et netus et malesuada +fames ac turpis egestas. In hac habitasse platea dictumst. Suspendisse +vel lorem ut ligula tempor consequat. Quisque consectetuer nisl eget +elit. +Proin quis mauris ac orci accumsan suscipit. Sed ipsum. Sed vel libero +nec elit feugiat blandit. Vestibulum purus nulla, accumsan et, +volutpat at, pellentesque vel, urna. Suspendisse nonummy. Aliquam +pulvinar libero. Donec vulputate, orci ornare bibendum condimentum, +lorem elit dignissim sapien, ut aliquam nibh augue in turpis. +Phasellus ac eros. Praesent luctus, lorem a mollis lacinia, leo turpis +commodo sem, in lacinia mi quam et quam. Curabitur a libero vel tellus +mattis imperdiet. In congue, neque ut scelerisque bibendum, libero +lacus ullamcorper sapien, quis aliquet massa velit vel orci. Fusce in +nulla quis est cursus gravida. In nibh. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Integer fermentum pretium massa. Morbi +feugiat iaculis nunc. +Aenean aliquam pretium orci. Cum sociis natoque penatibus et magnis +dis parturient montes, nascetur ridiculus mus. Vivamus quis tellus vel +quam varius bibendum. Fusce est metus, feugiat at, porttitor et, +cursus quis, pede. Nam ut augue. Nulla posuere. Phasellus at dolor a +enim cursus vestibulum. Duis id nisi. Duis semper tellus ac nulla. +Vestibulum scelerisque lobortis dolor. Aenean a felis. Aliquam erat +volutpat. Donec a magna vitae pede sagittis lacinia. Cras vestibulum +diam ut arcu. Mauris a nunc. Duis sollicitudin erat sit amet turpis. +Proin at libero eu diam lobortis fermentum. Nunc lorem turpis, +imperdiet id, gravida eget, aliquet sed, purus. Ut vehicula laoreet +ante. +Mauris eu nunc. Sed sit amet elit nec ipsum aliquam egestas. Donec non +nibh. Cras sodales pretium massa. Praesent hendrerit est et risus. +Vivamus eget pede. Curabitur tristique scelerisque dui. Nullam +ullamcorper. Vivamus venenatis velit eget enim. Nunc eu nunc eget +felis malesuada fermentum. Quisque magna. Mauris ligula felis, luctus +a, aliquet nec, vulputate eget, magna. Quisque placerat diam sed arcu. +Praesent sollicitudin. Aliquam non sapien. Quisque id augue. Class +aptent taciti sociosqu ad litora torquent per conubia nostra, per +inceptos hymenaeos. Etiam lacus lectus, mollis quis, mattis nec, +commodo facilisis, nibh. Sed sodales sapien ac ante. Duis eget lectus +in nibh lacinia auctor. +Fusce interdum lectus non dui. Integer accumsan. Quisque quam. +Curabitur scelerisque imperdiet nisl. Suspendisse potenti. Nam massa +leo, iaculis sed, accumsan id, ultrices nec, velit. Suspendisse +potenti. Mauris bibendum, turpis ac viverra sollicitudin, metus massa +interdum orci, non imperdiet orci ante at ipsum. Etiam eget magna. +Mauris at tortor eu lectus tempor tincidunt. Phasellus justo purus, +pharetra ut, ultricies nec, consequat vel, nisi. Fusce vitae velit at +libero sollicitudin sodales. Aenean mi libero, ultrices id, suscipit +vitae, dapibus eu, metus. Aenean vestibulum nibh ac massa. Vivamus +vestibulum libero vitae purus. In hac habitasse platea dictumst. +Curabitur blandit nunc non arcu. +Ut nec nibh. Morbi quis leo vel magna commodo rhoncus. Donec congue +leo eu lacus. Pellentesque at erat id mi consequat congue. Praesent a +nisl ut diam interdum molestie. Fusce suscipit rhoncus sem. Donec +pretium. Aliquam molestie. Vivamus et justo at augue aliquet dapibus. +Pellentesque felis. +Morbi semper. In venenatis imperdiet neque. Donec auctor molestie +augue. Nulla id arcu sit amet dui lacinia convallis. Proin tincidunt. +Proin a ante. Nunc imperdiet augue. Nullam sit amet arcu. Quisque +laoreet viverra felis. Lorem ipsum dolor sit amet, consectetuer +adipiscing elit. In hac habitasse platea dictumst. Pellentesque +habitant morbi tristique senectus et netus et malesuada fames ac +turpis egestas. Class aptent taciti sociosqu ad litora torquent per +conubia nostra, per inceptos hymenaeos. Nullam nibh sapien, volutpat +ut, placerat quis, ornare at, lorem. Class aptent taciti sociosqu ad +litora torquent per conubia nostra, per inceptos hymenaeos. +Morbi dictum massa id libero. Ut neque. Phasellus tincidunt, nibh ut +tincidunt lacinia, lacus nulla aliquam mi, a interdum dui augue non +pede. Duis nunc magna, vulputate a, porta at, tincidunt a, nulla. +Praesent facilisis. Suspendisse sodales feugiat purus. Cras et justo a +mauris mollis imperdiet. Morbi erat mi, ultrices eget, aliquam +elementum, iaculis id, velit. In scelerisque enim sit amet turpis. Sed +aliquam, odio nonummy ullamcorper mollis, lacus nibh tempor dolor, sit +amet varius sem neque ac dui. Nunc et est eu massa eleifend mollis. +Mauris aliquet orci quis tellus. Ut mattis. +Praesent mollis consectetuer quam. Nulla nulla. Nunc accumsan, nunc +sit amet scelerisque porttitor, nibh pede lacinia justo, tristique +mattis purus eros non velit. Aenean sagittis commodo erat. Aliquam id +lacus. Morbi vulputate vestibulum elit." +$LOREM = $LOREM.gsub(/\s+/, ' ') +$LOREM = $LOREM.split(/[.]\s*/) + +def lorem(n) + return $LOREM[rand($LOREM.size - 20)..-1].join('. ')[0, n] +end + +$ALPHANUMERICS = [('0'..'9'),('A'..'Z'),('a'..'z')].map {|range| range.to_a}.flatten +$RANDOM_CACHE = ((0... 100).map { $ALPHANUMERICS[rand($ALPHANUMERICS.size)] }.join) +$PASSWORD = ((0... 8).map { $ALPHANUMERICS[rand($ALPHANUMERICS.size)] }.join) + +$UNIQUE = [ + 'IssuePriority#name', + 'ActsAsTaggableOn::Tag#name', + 'Version#name', + 'IssueStatus#name' + ] + +$UNIQUE = {} + +def unique(model_attr) + v = $UNIQUE[model_attr].to_i + 1 + $UNIQUE[model_attr] = v + + v = "#{model_attr.split('#')[0]}#{v}" + v << "@example.com" if model_attr.match(/#mail/) + + return v +end + +def random_string(model_attr, v) + return nil if !v + + return $PASSWORD if model_attr.match(/#password$/) + + # these are required to be unique + return unique(model_attr) if $UNIQUE.include?(model_attr) || model_attr.match(/#mail$/) + + return lorem(v.size) if v.match(/ /) + + nv = nil + l = v.size + + while nv.nil? + start = rand($RANDOM_CACHE.size / 3) + + if $RANDOM_CACHE.size >= start + l + nv = $RANDOM_CACHE[start, l] + else + $RANDOM_CACHE << ((0... (l * 3)).map { $ALPHANUMERICS[rand($ALPHANUMERICS.size)] }.join) + end + end + + return nv +end + + +namespace :redmine do + namespace :backlogs_plugin do + task :anonymize => :environment do + puts "This will anonymize ALL YOUR DATA" + puts "ARE YOU VERY, VERY SURE?" + puts "If so, type 'Yes!' (case matters!)" + + answer = STDIN.gets.chomp + return if answer != "Yes!" + + ignore = [ + 'AnonymousUser#language', + 'Version#status', + 'Version#sharing', + 'CustomField#regexp', + 'CustomField#field_format', + 'Principal#language', + 'JournalDetail#property', + 'JournalDetail#prop_key', + 'Query#column_names', + 'Query#group_by', + 'Query#sort_criteria', + 'Query#filters', + 'Enumeration#name', + 'WikiContent::Version#compression', + 'User#language', + 'AnonymousUser#login', + ] + + admins = [] + ActiveRecord::Base.send(:subclasses).each do |model| + attrs = {} + model.columns_hash.each_pair { |attrib, column| + #next unless model.content_columns.include?(column) + next if column.name == 'type' + next if attrib.match(/_type$/) + next if [:integer, :boolean, :datetime, :date, :float].include?(column.type) + next if ignore.include?("#{model.name}##{attrib}") + + attrib = 'password' if attrib == 'hashed_password' + + attrs[attrib] = nil + } + + if attrs.size != 0 + puts "Anonymizing #{model.name} ..." + model.all.each { |obj| + save_error = nil + 10.times do |attempt| + attrs.each_pair {|k, v| + attrs[k] = random_string("#{model.name}##{k}", obj.send(k)) + } + + attempt_string = (attempt == 0 ? '' : " (attempt #{attempt + 1}") + + puts "... #{model.name} #{obj.id}#{attempt_string}" + + begin + obj.update_attributes(attrs) + obj.save! + save_error = nil + break + rescue ActiveRecord::RecordInvalid => save_error + end + end + + raise "Not able to save #{model.name} #{obj.id}: #{save_error}" if save_error + + if model.name == 'User' && obj.admin? + admins << obj.login + end + } + end + end + + User.find(:all).each { |user| + user.password = $PASSWORD + user.save! + } + + puts "Your anonymized admins are #{admins.inspect} with password '#{$PASSWORD}'" + end + end +end